,
+ getFilterEventData: (series: SeriesIdentifier) => ClickTriggerEvent | null,
+ onFilter: (data: ClickTriggerEvent, negate?: any) => void,
+ visParams: PieVisParams,
+ actions: DataPublicPluginStart['actions'],
+ formatter: DataPublicPluginStart['fieldFormats']
+): LegendAction => {
+ return ({ series: [pieSeries] }) => {
+ const [popoverOpen, setPopoverOpen] = useState(false);
+ const [isfilterable, setIsfilterable] = useState(true);
+ const filterData = getFilterEventData(pieSeries);
+
+ useEffect(() => {
+ (async () => setIsfilterable(await canFilter(filterData, actions)))();
+ }, [filterData]);
+
+ if (!isfilterable || !filterData) {
+ return null;
+ }
+
+ let formattedTitle = '';
+ if (visParams.dimensions.buckets) {
+ const column = visParams.dimensions.buckets.find(
+ (bucket) => bucket.accessor === filterData.data.data[0].column
+ );
+ formattedTitle = formatter.deserialize(column?.format).convert(pieSeries.key) ?? '';
+ }
+
+ const title = formattedTitle || pieSeries.key;
+ const panels: EuiContextMenuPanelDescriptor[] = [
+ {
+ id: 'main',
+ title: `${title}`,
+ items: [
+ {
+ name: i18n.translate('visTypePie.legend.filterForValueButtonAriaLabel', {
+ defaultMessage: 'Filter for value',
+ }),
+ 'data-test-subj': `legend-${title}-filterIn`,
+ icon: ,
+ onClick: () => {
+ setPopoverOpen(false);
+ onFilter(filterData);
+ },
+ },
+ {
+ name: i18n.translate('visTypePie.legend.filterOutValueButtonAriaLabel', {
+ defaultMessage: 'Filter out value',
+ }),
+ 'data-test-subj': `legend-${title}-filterOut`,
+ icon: ,
+ onClick: () => {
+ setPopoverOpen(false);
+ onFilter(filterData, true);
+ },
+ },
+ ],
+ },
+ ];
+
+ const Button = (
+ undefined}
+ onClick={() => setPopoverOpen(!popoverOpen)}
+ >
+
+
+ );
+
+ return (
+
+ );
+ };
+};
diff --git a/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts b/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts
new file mode 100644
index 00000000000000..e1029b11a7b758
--- /dev/null
+++ b/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+import { AccessorFn } from '@elastic/charts';
+import { FieldFormatsStart } from '../../../data/public';
+import { DatatableColumn } from '../../../expressions/public';
+import { Dimension } from '../types';
+
+export const getSplitDimensionAccessor = (
+ fieldFormats: FieldFormatsStart,
+ columns: DatatableColumn[]
+) => (splitDimension: Dimension): AccessorFn => {
+ const formatter = fieldFormats.deserialize(splitDimension.format);
+ const splitChartColumn = columns[splitDimension.accessor];
+ const accessor = splitChartColumn.id;
+
+ const fn: AccessorFn = (d) => {
+ const v = d[accessor];
+ if (v === undefined) {
+ return;
+ }
+ const f = formatter.convert(v);
+ return f;
+ };
+
+ return fn;
+};
diff --git a/src/plugins/vis_type_pie/public/utils/index.ts b/src/plugins/vis_type_pie/public/utils/index.ts
new file mode 100644
index 00000000000000..0cf4292ad565a9
--- /dev/null
+++ b/src/plugins/vis_type_pie/public/utils/index.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export { getLayers } from './get_layers';
+export { getColorPicker } from './get_color_picker';
+export { getLegendActions } from './get_legend_actions';
+export { canFilter, getFilterClickData, getFilterEventData } from './filter_helpers';
+export { getConfig } from './get_config';
+export { getColumns } from './get_columns';
+export { getSplitDimensionAccessor } from './get_split_dimension_accessor';
+export { getDistinctSeries } from './get_distinct_series';
diff --git a/src/plugins/vis_type_pie/public/vis_type/index.ts b/src/plugins/vis_type_pie/public/vis_type/index.ts
new file mode 100644
index 00000000000000..e02e802028a352
--- /dev/null
+++ b/src/plugins/vis_type_pie/public/vis_type/index.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { getPieVisTypeDefinition } from './pie';
+import type { PieTypeProps } from '../types';
+
+export const pieVisType = (props: PieTypeProps) => {
+ return getPieVisTypeDefinition(props);
+};
diff --git a/src/plugins/vis_type_pie/public/vis_type/pie.ts b/src/plugins/vis_type_pie/public/vis_type/pie.ts
new file mode 100644
index 00000000000000..9d1556ac33ad7e
--- /dev/null
+++ b/src/plugins/vis_type_pie/public/vis_type/pie.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { Position } from '@elastic/charts';
+import { AggGroupNames } from '../../../data/public';
+import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../../visualizations/public';
+import { DEFAULT_PERCENT_DECIMALS } from '../../common';
+import { PieVisParams, LabelPositions, ValueFormats, PieTypeProps } from '../types';
+import { toExpressionAst } from '../to_ast';
+import { getPieOptions } from '../editor/components';
+
+export const getPieVisTypeDefinition = ({
+ showElasticChartsOptions = false,
+ palettes,
+ trackUiMetric,
+}: PieTypeProps): VisTypeDefinition => ({
+ name: 'pie',
+ title: i18n.translate('visTypePie.pie.pieTitle', { defaultMessage: 'Pie' }),
+ icon: 'visPie',
+ description: i18n.translate('visTypePie.pie.pieDescription', {
+ defaultMessage: 'Compare data in proportion to a whole.',
+ }),
+ toExpressionAst,
+ getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter],
+ visConfig: {
+ defaults: {
+ type: 'pie',
+ addTooltip: true,
+ addLegend: !showElasticChartsOptions,
+ legendPosition: Position.Right,
+ nestedLegend: false,
+ distinctColors: false,
+ isDonut: true,
+ palette: {
+ type: 'palette',
+ name: 'default',
+ },
+ labels: {
+ show: true,
+ last_level: !showElasticChartsOptions,
+ values: true,
+ valuesFormat: ValueFormats.PERCENT,
+ percentDecimals: DEFAULT_PERCENT_DECIMALS,
+ truncate: 100,
+ position: LabelPositions.DEFAULT,
+ },
+ },
+ },
+ editorConfig: {
+ optionsTemplate: getPieOptions({
+ showElasticChartsOptions,
+ palettes,
+ trackUiMetric,
+ }),
+ schemas: [
+ {
+ group: AggGroupNames.Metrics,
+ name: 'metric',
+ title: i18n.translate('visTypePie.pie.metricTitle', {
+ defaultMessage: 'Slice size',
+ }),
+ min: 1,
+ max: 1,
+ aggFilter: ['sum', 'count', 'cardinality', 'top_hits'],
+ defaults: [{ schema: 'metric', type: 'count' }],
+ },
+ {
+ group: AggGroupNames.Buckets,
+ name: 'segment',
+ title: i18n.translate('visTypePie.pie.segmentTitle', {
+ defaultMessage: 'Split slices',
+ }),
+ min: 0,
+ max: Infinity,
+ aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
+ },
+ {
+ group: AggGroupNames.Buckets,
+ name: 'split',
+ title: i18n.translate('visTypePie.pie.splitTitle', {
+ defaultMessage: 'Split chart',
+ }),
+ mustBeFirst: true,
+ min: 0,
+ max: 1,
+ aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
+ },
+ ],
+ },
+ hierarchicalData: true,
+ requiresSearch: true,
+});
diff --git a/src/plugins/vis_type_pie/tsconfig.json b/src/plugins/vis_type_pie/tsconfig.json
new file mode 100644
index 00000000000000..f12db316f19723
--- /dev/null
+++ b/src/plugins/vis_type_pie/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "./target/types",
+ "emitDeclarationOnly": true,
+ "declaration": true,
+ "declarationMap": true
+ },
+ "include": [
+ "common/**/*",
+ "public/**/*",
+ "server/**/*"
+ ],
+ "references": [
+ { "path": "../../core/tsconfig.json" },
+ { "path": "../charts/tsconfig.json" },
+ { "path": "../data/tsconfig.json" },
+ { "path": "../expressions/tsconfig.json" },
+ { "path": "../visualizations/tsconfig.json" },
+ { "path": "../usage_collection/tsconfig.json" },
+ { "path": "../vis_default_editor/tsconfig.json" },
+ ]
+ }
\ No newline at end of file
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js
index 2877373ffba9a1..16e7b9d6072cba 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js
@@ -7,11 +7,11 @@
*/
import { getBucketsPath } from './get_buckets_path';
-import { parseInterval } from './parse_interval';
import { set } from '@elastic/safer-lodash-set';
import { isEmpty } from 'lodash';
import { i18n } from '@kbn/i18n';
import { MODEL_SCRIPTS } from './moving_fn_scripts';
+import { convertIntervalToUnit } from './unit_to_seconds';
function checkMetric(metric, fields) {
fields.forEach((field) => {
@@ -161,19 +161,24 @@ export const bucketTransform = {
};
},
- derivative: (bucket, metrics, bucketSize) => {
+ derivative: (bucket, metrics, intervalString) => {
checkMetric(bucket, ['type', 'field']);
+
const body = {
derivative: {
buckets_path: getBucketsPath(bucket.field, metrics),
gap_policy: 'skip', // seems sane
- unit: bucketSize,
+ unit: intervalString,
},
};
+
if (bucket.gap_policy) body.derivative.gap_policy = bucket.gap_policy;
if (bucket.unit) {
- body.derivative.unit = /^([\d]+)([shmdwMy]|ms)$/.test(bucket.unit) ? bucket.unit : bucketSize;
+ body.derivative.unit = /^([\d]+)([shmdwMy]|ms)$/.test(bucket.unit)
+ ? bucket.unit
+ : intervalString;
}
+
return body;
},
@@ -214,8 +219,10 @@ export const bucketTransform = {
};
},
- calculation: (bucket, metrics, bucketSize) => {
+ calculation: (bucket, metrics, intervalString) => {
checkMetric(bucket, ['variables', 'script']);
+ const inMsInterval = convertIntervalToUnit(intervalString, 'ms');
+
const body = {
bucket_script: {
buckets_path: bucket.variables.reduce((acc, row) => {
@@ -226,7 +233,7 @@ export const bucketTransform = {
source: bucket.script,
lang: 'painless',
params: {
- _interval: parseInterval(bucketSize).asMilliseconds(),
+ _interval: inMsInterval?.value,
},
},
gap_policy: 'skip', // seems sane
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js
index f82f332df19fd1..253612c0274ad9 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js
@@ -29,17 +29,20 @@ export function dateHistogram(
const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET);
const { timeField, interval, maxBars } = getIntervalAndTimefield(panel, series, seriesIndex);
- const { bucketSize, intervalString } = getBucketSize(
- req,
- interval,
- capabilities,
- maxBars ? Math.min(maxBarsUiSettings, maxBars) : barTargetUiSettings
- );
+ const { from, to } = offsetTime(req, series.offset_time);
- const getDateHistogramForLastBucketMode = () => {
- const { from, to } = offsetTime(req, series.offset_time);
+ let bucketInterval;
+
+ const overwriteDateHistogramForLastBucketMode = () => {
const { timezone } = capabilities;
+ const { intervalString } = getBucketSize(
+ req,
+ interval,
+ capabilities,
+ maxBars ? Math.min(maxBarsUiSettings, maxBars) : barTargetUiSettings
+ );
+
overwrite(doc, `aggs.${series.id}.aggs.timeseries.date_histogram`, {
field: timeField,
min_doc_count: 0,
@@ -50,25 +53,29 @@ export function dateHistogram(
},
...dateHistogramInterval(intervalString),
});
+
+ bucketInterval = intervalString;
};
- const getDateHistogramForEntireTimerangeMode = () =>
+ const overwriteDateHistogramForEntireTimerangeMode = () => {
overwrite(doc, `aggs.${series.id}.aggs.timeseries.auto_date_histogram`, {
field: timeField,
buckets: 1,
});
+ bucketInterval = `${to.valueOf() - from.valueOf()}ms`;
+ };
+
isLastValueTimerangeMode(panel, series)
- ? getDateHistogramForLastBucketMode()
- : getDateHistogramForEntireTimerangeMode();
+ ? overwriteDateHistogramForLastBucketMode()
+ : overwriteDateHistogramForEntireTimerangeMode();
overwrite(doc, `aggs.${series.id}.meta`, {
timeField,
- intervalString,
- bucketSize,
+ panelId: panel.id,
seriesId: series.id,
+ intervalString: bucketInterval,
index: panel.use_kibana_indexes ? seriesIndex.indexPattern?.id : undefined,
- panelId: panel.id,
});
return next(doc);
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js
index 741eb93267f4c2..2cd7a213b273e9 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js
@@ -86,7 +86,6 @@ describe('dateHistogram(req, panel, series)', () => {
},
},
meta: {
- bucketSize: 10,
intervalString: '10s',
timeField: '@timestamp',
seriesId: 'test',
@@ -128,7 +127,6 @@ describe('dateHistogram(req, panel, series)', () => {
},
},
meta: {
- bucketSize: 10,
intervalString: '10s',
timeField: '@timestamp',
seriesId: 'test',
@@ -173,7 +171,6 @@ describe('dateHistogram(req, panel, series)', () => {
},
},
meta: {
- bucketSize: 20,
intervalString: '20s',
timeField: 'timestamp',
seriesId: 'test',
@@ -185,8 +182,11 @@ describe('dateHistogram(req, panel, series)', () => {
});
describe('dateHistogram for entire time range mode', () => {
- test('should ignore entire range mode for timeseries', async () => {
+ beforeEach(() => {
panel.time_range_mode = 'entire_time_range';
+ });
+
+ test('should ignore entire range mode for timeseries', async () => {
panel.type = 'timeseries';
const next = (doc) => doc;
@@ -204,9 +204,36 @@ describe('dateHistogram(req, panel, series)', () => {
expect(doc.aggs.test.aggs.timeseries.date_histogram).toBeDefined();
});
- test('should returns valid date histogram for entire range mode', async () => {
- panel.time_range_mode = 'entire_time_range';
+ test('should set meta values', async () => {
+ // set 15 minutes (=== 900000ms) interval;
+ req.body.timerange = {
+ min: '2021-01-01T00:00:00Z',
+ max: '2021-01-01T00:15:00Z',
+ };
+
+ const next = (doc) => doc;
+ const doc = await dateHistogram(
+ req,
+ panel,
+ series,
+ config,
+ indexPattern,
+ capabilities,
+ uiSettings
+ )(next)({});
+ expect(doc.aggs.test.meta).toMatchInlineSnapshot(`
+ Object {
+ "index": undefined,
+ "intervalString": "900000ms",
+ "panelId": "panelId",
+ "seriesId": "test",
+ "timeField": "@timestamp",
+ }
+ `);
+ });
+
+ test('should returns valid date histogram for entire range mode', async () => {
const next = (doc) => doc;
const doc = await dateHistogram(
req,
@@ -232,8 +259,7 @@ describe('dateHistogram(req, panel, series)', () => {
meta: {
timeField: '@timestamp',
seriesId: 'test',
- bucketSize: 10,
- intervalString: '10s',
+ intervalString: '3600000ms',
panelId: 'panelId',
},
},
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js
index 29a11bf163e0be..33c6622f739416 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js
@@ -7,30 +7,17 @@
*/
import { overwrite } from '../../helpers';
-import { getBucketSize } from '../../helpers/get_bucket_size';
import { bucketTransform } from '../../helpers/bucket_transform';
-import { getIntervalAndTimefield } from '../../get_interval_and_timefield';
-import { UI_SETTINGS } from '../../../../../../data/common';
+import { get } from 'lodash';
-export function metricBuckets(
- req,
- panel,
- series,
- esQueryConfig,
- seriesIndex,
- capabilities,
- uiSettings
-) {
+export function metricBuckets(req, panel, series) {
return (next) => async (doc) => {
- const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET);
-
- const { interval } = getIntervalAndTimefield(panel, series, seriesIndex);
- const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings);
-
series.metrics
.filter((row) => !/_bucket$/.test(row.type) && !/^series/.test(row.type))
.forEach((metric) => {
const fn = bucketTransform[metric.type];
+ const intervalString = get(doc, `aggs.${series.id}.meta.intervalString`);
+
if (fn) {
try {
const bucket = fn(metric, series.metrics, intervalString);
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js
index dbeb3b1393bd52..c3075dd6dcac00 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js
@@ -6,39 +6,28 @@
* Side Public License, v 1.
*/
+import { get } from 'lodash';
import { overwrite } from '../../helpers';
-import { getBucketSize } from '../../helpers/get_bucket_size';
import { bucketTransform } from '../../helpers/bucket_transform';
-import { getIntervalAndTimefield } from '../../get_interval_and_timefield';
-import { UI_SETTINGS } from '../../../../../../data/common';
-export function siblingBuckets(
- req,
- panel,
- series,
- esQueryConfig,
- seriesIndex,
- capabilities,
- uiSettings
-) {
+export function siblingBuckets(req, panel, series) {
return (next) => async (doc) => {
- const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET);
- const { interval } = getIntervalAndTimefield(panel, series, seriesIndex);
- const { bucketSize } = getBucketSize(req, interval, capabilities, barTargetUiSettings);
-
series.metrics
.filter((row) => /_bucket$/.test(row.type))
.forEach((metric) => {
const fn = bucketTransform[metric.type];
+ const intervalString = get(doc, `aggs.${series.id}.meta.intervalString`);
+
if (fn) {
try {
- const bucket = fn(metric, series.metrics, bucketSize);
+ const bucket = fn(metric, series.metrics, intervalString);
overwrite(doc, `aggs.${series.id}.aggs.${metric.id}`, bucket);
} catch (e) {
// meh
}
}
});
+
return next(doc);
};
}
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js
index 3e883abc9e5e08..92ac4078a3835a 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js
@@ -20,6 +20,7 @@ export function dateHistogram(req, panel, esQueryConfig, seriesIndex, capabiliti
return (next) => async (doc) => {
const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET);
const { timeField, interval } = getIntervalAndTimefield(panel, {}, seriesIndex);
+ const { from, to } = getTimerange(req);
const meta = {
timeField,
@@ -27,14 +28,8 @@ export function dateHistogram(req, panel, esQueryConfig, seriesIndex, capabiliti
panelId: panel.id,
};
- const getDateHistogramForLastBucketMode = () => {
- const { bucketSize, intervalString } = getBucketSize(
- req,
- interval,
- capabilities,
- barTargetUiSettings
- );
- const { from, to } = getTimerange(req);
+ const overwriteDateHistogramForLastBucketMode = () => {
+ const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings);
const { timezone } = capabilities;
panel.series.forEach((column) => {
@@ -54,12 +49,13 @@ export function dateHistogram(req, panel, esQueryConfig, seriesIndex, capabiliti
overwrite(doc, aggRoot.replace(/\.aggs$/, '.meta'), {
...meta,
intervalString,
- bucketSize,
});
});
};
- const getDateHistogramForEntireTimerangeMode = () => {
+ const overwriteDateHistogramForEntireTimerangeMode = () => {
+ const intervalString = `${to.valueOf() - from.valueOf()}ms`;
+
panel.series.forEach((column) => {
const aggRoot = calculateAggRoot(doc, column);
@@ -68,13 +64,16 @@ export function dateHistogram(req, panel, esQueryConfig, seriesIndex, capabiliti
buckets: 1,
});
- overwrite(doc, aggRoot.replace(/\.aggs$/, '.meta'), meta);
+ overwrite(doc, aggRoot.replace(/\.aggs$/, '.meta'), {
+ ...meta,
+ intervalString,
+ });
});
};
isLastValueTimerangeMode(panel)
- ? getDateHistogramForLastBucketMode()
- : getDateHistogramForEntireTimerangeMode();
+ ? overwriteDateHistogramForLastBucketMode()
+ : overwriteDateHistogramForEntireTimerangeMode();
return next(doc);
};
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.js
index 421f9d2d75f0c6..8e0d0060225fff 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.js
@@ -6,19 +6,13 @@
* Side Public License, v 1.
*/
+import { get } from 'lodash';
import { overwrite } from '../../helpers';
-import { getBucketSize } from '../../helpers/get_bucket_size';
import { bucketTransform } from '../../helpers/bucket_transform';
-import { getIntervalAndTimefield } from '../../get_interval_and_timefield';
import { calculateAggRoot } from './calculate_agg_root';
-import { UI_SETTINGS } from '../../../../../../data/common';
-export function metricBuckets(req, panel, esQueryConfig, seriesIndex, capabilities, uiSettings) {
+export function metricBuckets(req, panel) {
return (next) => async (doc) => {
- const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET);
- const { interval } = getIntervalAndTimefield(panel, {}, seriesIndex);
- const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings);
-
panel.series.forEach((column) => {
const aggRoot = calculateAggRoot(doc, column);
column.metrics
@@ -27,7 +21,9 @@ export function metricBuckets(req, panel, esQueryConfig, seriesIndex, capabiliti
const fn = bucketTransform[metric.type];
if (fn) {
try {
+ const intervalString = get(doc, aggRoot.replace(/\.aggs$/, '.meta.intervalString'));
const bucket = fn(metric, column.metrics, intervalString);
+
overwrite(doc, `${aggRoot}.timeseries.aggs.${metric.id}`, bucket);
} catch (e) {
// meh
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.js
index 9b4b0f244fc2c1..6ce956c490900f 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.js
@@ -7,18 +7,12 @@
*/
import { overwrite } from '../../helpers';
-import { getBucketSize } from '../../helpers/get_bucket_size';
import { bucketTransform } from '../../helpers/bucket_transform';
-import { getIntervalAndTimefield } from '../../get_interval_and_timefield';
import { calculateAggRoot } from './calculate_agg_root';
-import { UI_SETTINGS } from '../../../../../../data/common';
+import { get } from 'lodash';
-export function siblingBuckets(req, panel, esQueryConfig, seriesIndex, capabilities, uiSettings) {
+export function siblingBuckets(req, panel) {
return (next) => async (doc) => {
- const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET);
- const { interval } = getIntervalAndTimefield(panel, {}, seriesIndex);
- const { bucketSize } = getBucketSize(req, interval, capabilities, barTargetUiSettings);
-
panel.series.forEach((column) => {
const aggRoot = calculateAggRoot(doc, column);
column.metrics
@@ -27,7 +21,9 @@ export function siblingBuckets(req, panel, esQueryConfig, seriesIndex, capabilit
const fn = bucketTransform[metric.type];
if (fn) {
try {
- const bucket = fn(metric, column.metrics, bucketSize);
+ const intervalString = get(doc, aggRoot.replace(/\.aggs$/, '.meta.intervalString'));
+ const bucket = fn(metric, column.metrics, intervalString);
+
overwrite(doc, `${aggRoot}.${metric.id}`, bucket);
} catch (e) {
// meh
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js
index 403b486cc4d091..d3cff76524ee36 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js
@@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
+import { convertIntervalToUnit } from '../../helpers/unit_to_seconds';
+
const percentileValueMatch = /\[([0-9\.]+)\]$/;
import { startsWith, flatten, values, first, last } from 'lodash';
import { getDefaultDecoration } from '../../helpers/get_default_decoration';
@@ -82,13 +84,15 @@ export function mathAgg(resp, panel, series, meta, extractFields) {
if (someNull) return [ts, null];
try {
// calculate the result based on the user's script and return the value
+ const inMsInterval = convertIntervalToUnit(split.meta?.intervalString || 0, 'ms');
+
const result = evaluate(mathMetric.script, {
params: {
...params,
_index: index,
_timestamp: ts,
_all: all,
- _interval: split.meta.bucketSize * 1000,
+ _interval: inMsInterval?.value,
},
});
// if the result is an object (usually when the user is working with maps and functions) flatten the results and return the last value.
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js
index 1e30720d6e5b21..7b5eb1e029069f 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js
@@ -54,7 +54,7 @@ describe('math(resp, panel, series)', () => {
aggregations: {
test: {
meta: {
- bucketSize: 5,
+ intervalString: '5s',
},
buckets: [
{
@@ -124,6 +124,25 @@ describe('math(resp, panel, series)', () => {
);
});
+ test('should works with predefined variables (params._interval)', async () => {
+ const expectedInterval = 5000;
+
+ series.metrics[2].script = 'params._interval';
+
+ const next = await mathAgg(resp, panel, series)((results) => results);
+ const results = await stdMetric(resp, panel, series)(next)([]);
+
+ expect(results).toHaveLength(1);
+ expect(results[0]).toEqual(
+ expect.objectContaining({
+ data: [
+ [1, expectedInterval],
+ [2, expectedInterval],
+ ],
+ })
+ );
+ });
+
test('throws on actual tinymath expression errors #1', async () => {
series.metrics[2].script = 'notExistingFn(params.a)';
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts
index 5b865d451003a9..46acbb27e15e14 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts
@@ -153,7 +153,6 @@ describe('buildRequestBody(req)', () => {
time_zone: 'UTC',
},
meta: {
- bucketSize: 10,
intervalString: '10s',
seriesId: 'c9b5f9c0-e403-11e6-be91-6f7688e9fac7',
timeField: '@timestamp',
diff --git a/src/plugins/vis_type_vislib/kibana.json b/src/plugins/vis_type_vislib/kibana.json
index 175c21f47c182a..56dfba0aca59c0 100644
--- a/src/plugins/vis_type_vislib/kibana.json
+++ b/src/plugins/vis_type_vislib/kibana.json
@@ -4,5 +4,5 @@
"server": true,
"ui": true,
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"],
- "requiredBundles": ["kibanaUtils", "visDefaultEditor", "visTypeXy"]
+ "requiredBundles": ["kibanaUtils", "visDefaultEditor", "visTypeXy", "visTypePie"]
}
diff --git a/src/plugins/vis_type_vislib/public/editor/components/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/index.tsx
index a90aaeab58503d..34547dc7115e28 100644
--- a/src/plugins/vis_type_vislib/public/editor/components/index.tsx
+++ b/src/plugins/vis_type_vislib/public/editor/components/index.tsx
@@ -10,21 +10,15 @@ import React, { lazy } from 'react';
import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { GaugeVisParams } from '../../gauge';
-import { PieVisParams } from '../../pie';
import { HeatmapVisParams } from '../../heatmap';
const GaugeOptionsLazy = lazy(() => import('./gauge'));
-const PieOptionsLazy = lazy(() => import('./pie'));
const HeatmapOptionsLazy = lazy(() => import('./heatmap'));
export const GaugeOptions = (props: VisEditorOptionsProps) => (
);
-export const PieOptions = (props: VisEditorOptionsProps) => (
-
-);
-
export const HeatmapOptions = (props: VisEditorOptionsProps) => (
);
diff --git a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx
deleted file mode 100644
index 6c84bc744676a9..00000000000000
--- a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import React from 'react';
-
-import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-
-import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
-import { BasicOptions, SwitchOption } from '../../../../vis_default_editor/public';
-import { TruncateLabelsOption, getPositions } from '../../../../vis_type_xy/public';
-
-import { PieVisParams } from '../../pie';
-
-const legendPositions = getPositions();
-
-function PieOptions(props: VisEditorOptionsProps) {
- const { stateParams, setValue } = props;
- const setLabels = (
- paramName: T,
- value: PieVisParams['labels'][T]
- ) => setValue('labels', { ...stateParams.labels, [paramName]: value });
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-// default export required for React.Lazy
-// eslint-disable-next-line import/no-default-export
-export { PieOptions as default };
diff --git a/src/plugins/vis_type_vislib/public/pie.ts b/src/plugins/vis_type_vislib/public/pie.ts
index d1d8d2a5279feb..4f6eb7e5365092 100644
--- a/src/plugins/vis_type_vislib/public/pie.ts
+++ b/src/plugins/vis_type_vislib/public/pie.ts
@@ -6,14 +6,9 @@
* Side Public License, v 1.
*/
-import { i18n } from '@kbn/i18n';
-import { Position } from '@elastic/charts';
-
-import { AggGroupNames } from '../../data/public';
-import { VisTypeDefinition, VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
-
+import { pieVisType } from '../../vis_type_pie/public';
+import { VisTypeDefinition } from '../../visualizations/public';
import { CommonVislibParams } from './types';
-import { PieOptions } from './editor';
import { toExpressionAst } from './to_ast_pie';
export interface PieVisParams extends CommonVislibParams {
@@ -27,67 +22,7 @@ export interface PieVisParams extends CommonVislibParams {
};
}
-export const pieVisTypeDefinition: VisTypeDefinition = {
- name: 'pie',
- title: i18n.translate('visTypeVislib.pie.pieTitle', { defaultMessage: 'Pie' }),
- icon: 'visPie',
- description: i18n.translate('visTypeVislib.pie.pieDescription', {
- defaultMessage: 'Compare data in proportion to a whole.',
- }),
- getSupportedTriggers: () => [VIS_EVENT_TO_TRIGGER.filter],
+export const pieVisTypeDefinition = {
+ ...pieVisType({}),
toExpressionAst,
- visConfig: {
- defaults: {
- type: 'pie',
- addTooltip: true,
- addLegend: true,
- legendPosition: Position.Right,
- isDonut: true,
- labels: {
- show: false,
- values: true,
- last_level: true,
- truncate: 100,
- },
- },
- },
- editorConfig: {
- optionsTemplate: PieOptions,
- schemas: [
- {
- group: AggGroupNames.Metrics,
- name: 'metric',
- title: i18n.translate('visTypeVislib.pie.metricTitle', {
- defaultMessage: 'Slice size',
- }),
- min: 1,
- max: 1,
- aggFilter: ['sum', 'count', 'cardinality', 'top_hits'],
- defaults: [{ schema: 'metric', type: 'count' }],
- },
- {
- group: AggGroupNames.Buckets,
- name: 'segment',
- title: i18n.translate('visTypeVislib.pie.segmentTitle', {
- defaultMessage: 'Split slices',
- }),
- min: 0,
- max: Infinity,
- aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
- },
- {
- group: AggGroupNames.Buckets,
- name: 'split',
- title: i18n.translate('visTypeVislib.pie.splitTitle', {
- defaultMessage: 'Split chart',
- }),
- mustBeFirst: true,
- min: 0,
- max: 1,
- aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
- },
- ],
- },
- hierarchicalData: true,
- requiresSearch: true,
-};
+} as VisTypeDefinition;
diff --git a/src/plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_type_vislib/public/plugin.ts
index 9d329c92bede0c..52faf8a74778c3 100644
--- a/src/plugins/vis_type_vislib/public/plugin.ts
+++ b/src/plugins/vis_type_vislib/public/plugin.ts
@@ -13,7 +13,7 @@ import { VisualizationsSetup } from '../../visualizations/public';
import { ChartsPluginSetup } from '../../charts/public';
import { DataPublicPluginStart } from '../../data/public';
import { KibanaLegacyStart } from '../../kibana_legacy/public';
-import { LEGACY_CHARTS_LIBRARY } from '../../vis_type_xy/public';
+import { LEGACY_CHARTS_LIBRARY } from '../../visualizations/common/constants';
import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn';
import { createPieVisFn } from './pie_fn';
@@ -53,9 +53,8 @@ export class VisTypeVislibPlugin
if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) {
// Register only non-replaced vis types
convertedTypeDefinitions.forEach(visualizations.createBaseVisualization);
- visualizations.createBaseVisualization(pieVisTypeDefinition);
expressions.registerRenderer(getVislibVisRenderer(core, charts));
- [createVisTypeVislibVisFn(), createPieVisFn()].forEach(expressions.registerFunction);
+ expressions.registerFunction(createVisTypeVislibVisFn());
} else {
// Register all vis types
visLibVisTypeDefinitions.forEach(visualizations.createBaseVisualization);
diff --git a/src/plugins/vis_type_vislib/public/to_ast_pie.test.ts b/src/plugins/vis_type_vislib/public/to_ast_pie.test.ts
index 3ca52f27e3fa18..3178c23ee8fa0d 100644
--- a/src/plugins/vis_type_vislib/public/to_ast_pie.test.ts
+++ b/src/plugins/vis_type_vislib/public/to_ast_pie.test.ts
@@ -10,7 +10,7 @@ import { Vis } from '../../visualizations/public';
import { buildExpression } from '../../expressions/public';
import { PieVisParams } from './pie';
-import { samplePieVis } from '../../vis_type_xy/public/sample_vis.test.mocks';
+import { samplePieVis } from '../../vis_type_pie/public/sample_vis.test.mocks';
import { toExpressionAst } from './to_ast_pie';
jest.mock('../../expressions/public', () => ({
diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js
index cb8a8f72c51727..5e1f0bfbb44644 100644
--- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js
+++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js
@@ -31,6 +31,7 @@ export function pointSeriesTooltipFormatter() {
const details = [];
const isGauge = config.get('gauge', false);
+ const chartType = config.get('type', undefined);
const isPercentageMode = config.get(isGauge ? 'gauge.percentageMode' : 'percentageMode', false);
const isSetColorRange = config.get('setColorRange', false);
@@ -44,7 +45,8 @@ export function pointSeriesTooltipFormatter() {
});
}
- if (datum.x !== null && datum.x !== undefined) {
+ // For goal and gauge we have only one value for x - '_all'. It doesn't have sense to show it
+ if (datum.x !== null && datum.x !== undefined && !['goal', 'gauge'].includes(chartType)) {
addDetail(data.xAxisLabel, data.xAxisFormatter(datum.x));
}
diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js
index 5c0548ea399b75..a207b1f4360b6a 100644
--- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js
+++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js
@@ -96,4 +96,27 @@ describe('tooltipFormatter', function () {
const $rows = $el.find('tr');
expect($rows.length).toBe(3);
});
+
+ it('renders correctly for gauge/goal visualizations', function () {
+ const event = _.cloneDeep(baseEvent);
+ let type = 'gauge';
+ event.config.get = (name) => {
+ const config = {
+ setColorRange: false,
+ gauge: false,
+ percentageMode: false,
+ type,
+ };
+ return config[name];
+ };
+
+ let $el = $(tooltipFormatter(event, uiSettings));
+ let $rows = $el.find('tr');
+ expect($rows.length).toBe(2);
+
+ type = 'goal';
+ $el = $(tooltipFormatter(event, uiSettings));
+ $rows = $el.find('tr');
+ expect($rows.length).toBe(2);
+ });
});
diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts
index 71f692b80b531f..de91053b6dc4df 100644
--- a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts
+++ b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts
@@ -5,8 +5,8 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
-
-import { buildHierarchicalData, Dimensions, Dimension } from './build_hierarchical_data';
+import type { Dimensions, Dimension } from '../../../../../vis_type_pie/public';
+import { buildHierarchicalData } from './build_hierarchical_data';
import { Table, TableParent } from '../../types';
function tableVisResponseHandler(table: Table, dimensions: Dimensions) {
diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts
index b235d3936ae0fd..da10edf9591fbf 100644
--- a/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts
+++ b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts
@@ -7,24 +7,9 @@
*/
import { toArray } from 'lodash';
-import { SerializedFieldFormat } from '../../../../../expressions/common/types';
import { getFormatService } from '../../../services';
import { Table } from '../../types';
-
-export interface Dimension {
- accessor: number;
- format: {
- id?: string;
- params?: SerializedFieldFormat