diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_group_by_cardinality.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_group_by_cardinality.ts
new file mode 100644
index 0000000000000..928eb7e92482d
--- /dev/null
+++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_group_by_cardinality.ts
@@ -0,0 +1,69 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { ALL_VALUE } from '@kbn/slo-schema';
+import { useQuery } from '@tanstack/react-query';
+import { lastValueFrom } from 'rxjs';
+import { useKibana } from '../../utils/kibana_react';
+
+export interface UseFetchIndexPatternFieldsResponse {
+ isLoading: boolean;
+ isSuccess: boolean;
+ isError: boolean;
+ data?: { cardinality: number; isHighCardinality: boolean };
+}
+
+const HIGH_CARDINALITY_THRESHOLD = 1000;
+
+export function useFetchGroupByCardinality(
+ indexPattern: string,
+ timestampField: string,
+ groupBy: string
+): UseFetchIndexPatternFieldsResponse {
+ const { data: dataService } = useKibana().services;
+
+ const { isLoading, isError, isSuccess, data } = useQuery({
+ queryKey: ['fetchGroupByCardinality', indexPattern, timestampField, groupBy],
+ queryFn: async ({ signal }) => {
+ try {
+ const result = await lastValueFrom(
+ dataService.search.search({
+ params: {
+ index: indexPattern,
+ body: {
+ query: {
+ bool: {
+ filter: [{ range: { [timestampField]: { gte: 'now-24h' } } }],
+ },
+ },
+ aggs: {
+ groupByCardinality: {
+ cardinality: {
+ field: groupBy,
+ },
+ },
+ },
+ },
+ },
+ })
+ );
+
+ // @ts-expect-error Property 'value' does not exist on type 'AggregationsAggregate'
+ const cardinality = result.rawResponse?.aggregations?.groupByCardinality?.value ?? 0;
+ return { cardinality, isHighCardinality: cardinality > HIGH_CARDINALITY_THRESHOLD };
+ } catch (error) {
+ throw new Error(`Something went wrong. Error: ${error}`);
+ }
+ },
+ retry: false,
+ refetchOnWindowFocus: false,
+ enabled:
+ Boolean(indexPattern) && Boolean(timestampField) && Boolean(groupBy) && groupBy !== ALL_VALUE,
+ });
+
+ return { isLoading, isError, isSuccess, data };
+}
diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts
index 3363d501fae22..5aedb92219da6 100644
--- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts
+++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { FieldSpec } from '@kbn/data-views-plugin/common';
import { useQuery } from '@tanstack/react-query';
import { useKibana } from '../../utils/kibana_react';
@@ -12,14 +13,7 @@ export interface UseFetchIndexPatternFieldsResponse {
isLoading: boolean;
isSuccess: boolean;
isError: boolean;
- data: Field[] | undefined;
-}
-
-export interface Field {
- name: string;
- type: string;
- aggregatable: boolean;
- searchable: boolean;
+ data: FieldSpec[] | undefined;
}
export function useFetchIndexPatternFields(
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx
index c877d1de173c1..78afcc71cae58 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx
@@ -5,11 +5,12 @@
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
+import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
import React, { useEffect } from 'react';
import { useFormContext } from 'react-hook-form';
+import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
import { useFetchApmIndex } from '../../../../hooks/slo/use_fetch_apm_indices';
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { CreateSLOForm } from '../../types';
@@ -27,9 +28,14 @@ export function ApmAvailabilityIndicatorTypeForm() {
setValue('indicator.params.index', apmIndex);
}
}, [setValue, apmIndex]);
+ const timestampField = watch('indicator.params.timestampField');
+ const groupByField = watch('groupBy');
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(apmIndex);
+
+ const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
+ useFetchGroupByCardinality(apmIndex, timestampField, groupByField);
const groupByFields = indexFields.filter((field) => field.aggregatable);
return (
@@ -158,6 +164,19 @@ export function ApmAvailabilityIndicatorTypeForm() {
isDisabled={!apmIndex}
/>
+ {!isGroupByCardinalityLoading && !!groupByCardinality && (
+
+ )}
+
);
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx
index b75c9fd8d110c..dcb4be69d272d 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx
@@ -5,11 +5,19 @@
* 2.0.
*/
-import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiIconTip } from '@elastic/eui';
+import {
+ EuiCallOut,
+ EuiFieldNumber,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiFormRow,
+ EuiIconTip,
+} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
import React, { useEffect } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
+import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
import { useFetchApmIndex } from '../../../../hooks/slo/use_fetch_apm_indices';
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { CreateSLOForm } from '../../types';
@@ -28,8 +36,14 @@ export function ApmLatencyIndicatorTypeForm() {
}
}, [setValue, apmIndex]);
+ const timestampField = watch('indicator.params.timestampField');
+ const groupByField = watch('groupBy');
+
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(apmIndex);
+
+ const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
+ useFetchGroupByCardinality(apmIndex, timestampField, groupByField);
const groupByFields = indexFields.filter((field) => field.aggregatable);
return (
@@ -201,6 +215,19 @@ export function ApmLatencyIndicatorTypeForm() {
isDisabled={!apmIndex}
/>
+ {!isGroupByCardinalityLoading && !!groupByCardinality && (
+
+ )}
+
);
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx
index 367ff255edde0..0a07515a8915f 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx
@@ -6,14 +6,14 @@
*/
import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexItem, EuiFormRow } from '@elastic/eui';
+import { FieldSpec } from '@kbn/data-views-plugin/common';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { createOptionsFromFields, Option } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
interface Props {
- indexFields: Field[];
+ indexFields: FieldSpec[];
name: 'groupBy' | 'indicator.params.timestampField';
label: React.ReactNode | string;
placeholder: string;
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx
index 33b8a2eb6def3..42130cf204df6 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx
@@ -5,11 +5,12 @@
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
+import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
import React from 'react';
import { useFormContext } from 'react-hook-form';
+import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { CreateSLOForm } from '../../types';
import { DataPreviewChart } from '../common/data_preview_chart';
@@ -20,11 +21,17 @@ import { IndexSelection } from '../custom_common/index_selection';
export function CustomKqlIndicatorTypeForm() {
const { watch } = useFormContext();
const index = watch('indicator.params.index');
+ const timestampField = watch('indicator.params.timestampField');
+ const groupByField = watch('groupBy');
+
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(index);
const timestampFields = indexFields.filter((field) => field.type === 'date');
const groupByFields = indexFields.filter((field) => field.aggregatable);
+ const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
+ useFetchGroupByCardinality(index, timestampField, groupByField);
+
return (
@@ -158,6 +165,19 @@ export function CustomKqlIndicatorTypeForm() {
isDisabled={!index}
/>
+ {!isGroupByCardinalityLoading && !!groupByCardinality && (
+
+ )}
+
);
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx
index c7a180d59cfbe..2db17479ffd2d 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx
@@ -6,6 +6,7 @@
*/
import {
+ EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
@@ -18,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
import React from 'react';
import { useFormContext } from 'react-hook-form';
+import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { CreateSLOForm } from '../../types';
import { DataPreviewChart } from '../common/data_preview_chart';
@@ -33,8 +35,14 @@ const SUPPORTED_METRIC_FIELD_TYPES = ['number', 'histogram'];
export function CustomMetricIndicatorTypeForm() {
const { watch } = useFormContext();
const index = watch('indicator.params.index');
+ const timestampField = watch('indicator.params.timestampField');
+ const groupByField = watch('groupBy');
+
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(index);
+ const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
+ useFetchGroupByCardinality(index, timestampField, groupByField);
+
const timestampFields = indexFields.filter((field) => field.type === 'date');
const groupByFields = indexFields.filter((field) => field.aggregatable);
const metricFields = indexFields.filter((field) =>
@@ -175,6 +183,19 @@ export function CustomMetricIndicatorTypeForm() {
isDisabled={!index}
/>
+ {!isGroupByCardinalityLoading && !!groupByCardinality && (
+
+ )}
+
>
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx
index 262e6e6d2249a..3e077ab2280a6 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx
@@ -16,12 +16,12 @@ import {
EuiIconTip,
EuiSpacer,
} from '@elastic/eui';
+import { FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { first, range, xor } from 'lodash';
import React, { useEffect, useState } from 'react';
import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
-import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import {
aggValueToLabel,
CUSTOM_METRIC_AGGREGATION_OPTIONS,
@@ -32,7 +32,7 @@ import { QueryBuilder } from '../common/query_builder';
interface MetricIndicatorProps {
type: 'good' | 'total';
- metricFields: Field[];
+ metricFields: FieldSpec[];
isLoadingIndex: boolean;
}
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
index 1362674828d74..4efcc1bfb5b8b 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
@@ -15,17 +15,17 @@ import {
EuiIconTip,
EuiSpacer,
} from '@elastic/eui';
+import { FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import React, { Fragment, useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { createOptionsFromFields, Option } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
import { QueryBuilder } from '../common/query_builder';
interface HistogramIndicatorProps {
type: 'good' | 'total';
- histogramFields: Field[];
+ histogramFields: FieldSpec[];
isLoadingIndex: boolean;
}
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx
index 9f8831fa9dfab..992547dc2802f 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx
@@ -6,6 +6,7 @@
*/
import {
+ EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
@@ -18,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
import React from 'react';
import { useFormContext } from 'react-hook-form';
+import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { CreateSLOForm } from '../../types';
import { DataPreviewChart } from '../common/data_preview_chart';
@@ -29,9 +31,14 @@ import { HistogramIndicator } from './histogram_indicator';
export function HistogramIndicatorTypeForm() {
const { watch } = useFormContext();
const index = watch('indicator.params.index');
+ const timestampField = watch('indicator.params.timestampField');
+ const groupByField = watch('groupBy');
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(index);
+ const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
+ useFetchGroupByCardinality(index, timestampField, groupByField);
+
const histogramFields = indexFields.filter((field) => field.type === 'histogram');
const timestampFields = indexFields.filter((field) => field.type === 'date');
const groupByFields = indexFields.filter((field) => field.aggregatable);
@@ -162,6 +169,19 @@ export function HistogramIndicatorTypeForm() {
isDisabled={!index}
/>
+ {!isGroupByCardinalityLoading && !!groupByCardinality && (
+
+ )}
+
>
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx
index 14cce99bb5d86..b8105814b852e 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_indicator.tsx
@@ -17,18 +17,18 @@ import {
EuiSpacer,
EuiText,
} from '@elastic/eui';
+import { FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { first, range, xor } from 'lodash';
import React from 'react';
import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
-import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { COMPARATOR_OPTIONS } from '../../constants';
import { CreateSLOForm } from '../../types';
import { MetricInput } from './metric_input';
interface MetricIndicatorProps {
- indexFields: Field[];
+ indexFields: FieldSpec[];
isLoadingIndex: boolean;
}
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx
index 95cc277556020..22cbc42366baa 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx
@@ -12,10 +12,10 @@ import {
EuiFormRow,
EuiIconTip,
} from '@elastic/eui';
+import { FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { AGGREGATION_OPTIONS, aggValueToLabel } from '../../helpers/aggregation_options';
import { createOptionsFromFields, Option } from '../../helpers/create_options';
import { CreateSLOForm } from '../../types';
@@ -55,7 +55,7 @@ interface MetricInputProps {
metricIndex: number;
indexPattern: string;
isLoadingIndex: boolean;
- indexFields: Field[];
+ indexFields: FieldSpec[];
}
export function MetricInput({
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx
index 2fde2468d78ec..1c01219ffe1a2 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx
@@ -6,6 +6,7 @@
*/
import {
+ EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
@@ -19,6 +20,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common';
+import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality';
import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields';
import { CreateSLOForm } from '../../types';
import { DataPreviewChart } from '../common/data_preview_chart';
@@ -34,8 +36,14 @@ export { NEW_TIMESLICE_METRIC } from './metric_indicator';
export function TimesliceMetricIndicatorTypeForm() {
const { watch } = useFormContext();
const index = watch('indicator.params.index');
+ const timestampField = watch('indicator.params.timestampField');
+ const groupByField = watch('groupBy');
+
const { isLoading: isIndexFieldsLoading, data: indexFields = [] } =
useFetchIndexPatternFields(index);
+ const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } =
+ useFetchGroupByCardinality(index, timestampField, groupByField);
+
const timestampFields = indexFields.filter((field) => field.type === 'date');
const groupByFields = indexFields.filter((field) => field.aggregatable);
const { uiSettings } = useKibana().services;
@@ -152,6 +160,19 @@ export function TimesliceMetricIndicatorTypeForm() {
isDisabled={!index}
/>
+ {!isGroupByCardinalityLoading && !!groupByCardinality && (
+
+ )}
+
boolean
): Option[] {
const options = fields