diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md index 22dc92c275670..4b3c915b49c2d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md @@ -35,6 +35,17 @@ search: { siblingPipelineType: string; termsAggFilter: string[]; toAbsoluteDates: typeof toAbsoluteDates; + boundsDescendingRaw: ({ + bound: number; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + } | { + bound: import("moment").Duration; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + })[]; }; getRequestInspectorStats: typeof getRequestInspectorStats; getResponseInspectorStats: typeof getResponseInspectorStats; diff --git a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts index 83fd22a618fec..3c1a89015252e 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts @@ -6,75 +6,205 @@ * Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import moment from 'moment'; -const boundsDescending = [ +export const boundsDescendingRaw = [ { bound: Infinity, - interval: Number(moment.duration(1, 'year')), + interval: moment.duration(1, 'year'), + boundLabel: i18n.translate('data.search.timeBuckets.infinityLabel', { + defaultMessage: 'More than a year', + }), + intervalLabel: i18n.translate('data.search.timeBuckets.yearLabel', { + defaultMessage: 'a year', + }), }, { - bound: Number(moment.duration(1, 'year')), - interval: Number(moment.duration(1, 'month')), + bound: moment.duration(1, 'year'), + interval: moment.duration(1, 'month'), + boundLabel: i18n.translate('data.search.timeBuckets.yearLabel', { + defaultMessage: 'a year', + }), + intervalLabel: i18n.translate('data.search.timeBuckets.monthLabel', { + defaultMessage: 'a month', + }), }, { - bound: Number(moment.duration(3, 'week')), - interval: Number(moment.duration(1, 'week')), + bound: moment.duration(3, 'week'), + interval: moment.duration(1, 'week'), + boundLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 21 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 7 }, + }), }, { - bound: Number(moment.duration(1, 'week')), - interval: Number(moment.duration(1, 'd')), + bound: moment.duration(1, 'week'), + interval: moment.duration(1, 'd'), + boundLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 7 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(24, 'hour')), - interval: Number(moment.duration(12, 'hour')), + bound: moment.duration(24, 'hour'), + interval: moment.duration(12, 'hour'), + boundLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 1 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 12 }, + }), }, { - bound: Number(moment.duration(6, 'hour')), - interval: Number(moment.duration(3, 'hour')), + bound: moment.duration(6, 'hour'), + interval: moment.duration(3, 'hour'), + boundLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 6 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 3 }, + }), }, { - bound: Number(moment.duration(2, 'hour')), - interval: Number(moment.duration(1, 'hour')), + bound: moment.duration(2, 'hour'), + interval: moment.duration(1, 'hour'), + boundLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 2 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(45, 'minute')), - interval: Number(moment.duration(30, 'minute')), + bound: moment.duration(45, 'minute'), + interval: moment.duration(30, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 45 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 30 }, + }), }, { - bound: Number(moment.duration(20, 'minute')), - interval: Number(moment.duration(10, 'minute')), + bound: moment.duration(20, 'minute'), + interval: moment.duration(10, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 20 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 10 }, + }), }, { - bound: Number(moment.duration(9, 'minute')), - interval: Number(moment.duration(5, 'minute')), + bound: moment.duration(9, 'minute'), + interval: moment.duration(5, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 9 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 5 }, + }), }, { - bound: Number(moment.duration(3, 'minute')), - interval: Number(moment.duration(1, 'minute')), + bound: moment.duration(3, 'minute'), + interval: moment.duration(1, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 3 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(45, 'second')), - interval: Number(moment.duration(30, 'second')), + bound: moment.duration(45, 'second'), + interval: moment.duration(30, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 45 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 30 }, + }), }, { - bound: Number(moment.duration(15, 'second')), - interval: Number(moment.duration(10, 'second')), + bound: moment.duration(15, 'second'), + interval: moment.duration(10, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 15 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 10 }, + }), }, { - bound: Number(moment.duration(7.5, 'second')), - interval: Number(moment.duration(5, 'second')), + bound: moment.duration(7.5, 'second'), + interval: moment.duration(5, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 7.5 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 5 }, + }), }, { - bound: Number(moment.duration(5, 'second')), - interval: Number(moment.duration(1, 'second')), + bound: moment.duration(5, 'second'), + interval: moment.duration(1, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 5 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(500, 'ms')), - interval: Number(moment.duration(100, 'ms')), + bound: moment.duration(500, 'ms'), + interval: moment.duration(100, 'ms'), + boundLabel: i18n.translate('data.search.timeBuckets.millisecondLabel', { + defaultMessage: '{amount, plural, one {a millisecond} other {# milliseconds}}', + values: { amount: 500 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.millisecondLabel', { + defaultMessage: '{amount, plural, one {a millisecond} other {# milliseconds}}', + values: { amount: 100 }, + }), }, ]; +const boundsDescending = boundsDescendingRaw.map(({ bound, interval }) => ({ + bound: Number(bound), + interval: Number(interval), +})); + function getPerBucketMs(count: number, duration: number) { const ms = duration / count; return isFinite(ms) ? ms : NaN; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 9f0a5b64bde5a..ff3e2ebc89a41 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -307,6 +307,7 @@ import { parseEsInterval, parseInterval, toAbsoluteDates, + boundsDescendingRaw, // expressions utils getRequestInspectorStats, getResponseInspectorStats, @@ -416,6 +417,7 @@ export const search = { siblingPipelineType, termsAggFilter, toAbsoluteDates, + boundsDescendingRaw, }, getRequestInspectorStats, getResponseInspectorStats, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index dd24b1152b22c..e521e468d14a4 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2205,6 +2205,17 @@ export const search: { siblingPipelineType: string; termsAggFilter: string[]; toAbsoluteDates: typeof toAbsoluteDates; + boundsDescendingRaw: ({ + bound: number; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + } | { + bound: import("moment").Duration; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + })[]; }; getRequestInspectorStats: typeof getRequestInspectorStats; getResponseInspectorStats: typeof getResponseInspectorStats; @@ -2608,21 +2619,21 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:417:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:425:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:426:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:41:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 1bdffc90797ac..dc7b291b7120f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -299,6 +299,17 @@ export function DimensionEditor(props: DimensionEditorProps) { } ); + // Need to workout early on the error to decide whether to show this or an help text + const fieldErrorMessage = + (selectedOperationDefinition?.input !== 'fullReference' || + (incompleteOperation && operationDefinitionMap[incompleteOperation].input === 'field')) && + getErrorMessage( + selectedColumn, + Boolean(incompleteOperation), + selectedOperationDefinition?.input, + currentFieldIsInvalid + ); + return (
+
+
+
+
+
+
+ {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBasicExplanation', { + defaultMessage: 'The auto date histogram splits a date field into buckets by interval.', + })} +
+ +
+
+ {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation', { + defaultMessage: 'The interval follows this logic:', + })} +
+ ++ {i18n.translate('xpack.lens.indexPattern.ranges.granularityPopoverBasicExplanation', { + defaultMessage: + 'Interval granularity divides the field into evenly spaced intervals based on the minimum and maximum values for the field.', + })} +
+ +
+
+ {i18n.translate('xpack.lens.indexPattern.ranges.granularityPopoverAdvancedExplanation', { + defaultMessage: + 'Intervals are incremented by 10, 5 or 2: for example an interval can be 100 or 0.2 .', + })} +
+