diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.test.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.test.tsx
index 951602334a622..beb0e1f05e1b8 100644
--- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.test.tsx
+++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.test.tsx
@@ -830,4 +830,14 @@ describe('UnifiedFieldList FieldStats', () => {
expect(wrapper.text()).toBe('Summarymin29674max36821994Calculated from 5000 sample records.');
});
+
+ it('should not request field stats for ES|QL query', async () => {
+ const wrapper = await mountComponent(
+
+ );
+
+ expect(loadFieldStats).toHaveBeenCalledTimes(0);
+
+ expect(wrapper.text()).toBe('Analysis is not available for this field.');
+ });
});
diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx
index 8eada232cdeaf..58ff36069dd8c 100755
--- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx
+++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx
@@ -42,7 +42,6 @@ import {
canProvideNumberSummaryForField,
} from '../../utils/can_provide_stats';
import { loadFieldStats } from '../../services/field_stats';
-import { loadFieldStatsTextBased } from '../../services/field_stats_text_based';
import type { AddFieldFilterHandler } from '../../types';
import {
FieldTopValues,
@@ -136,7 +135,7 @@ const FieldStatsComponent: React.FC = ({
const [dataView, changeDataView] = useState(null);
const abortControllerRef = useRef(null);
const isCanceledRef = useRef(false);
- const isTextBased = !!query && isOfAggregateQueryType(query);
+ const isEsqlQuery = !!query && isOfAggregateQueryType(query);
const setState: typeof changeState = useCallback(
(nextState) => {
@@ -178,6 +177,12 @@ const FieldStatsComponent: React.FC = ({
setDataView(loadedDataView);
+ if (isEsqlQuery) {
+ // Not supported yet for ES|QL queries
+ // Previous implementation was removed in https://github.com/elastic/kibana/pull/198948/
+ return;
+ }
+
if (state.isLoading) {
return;
}
@@ -187,32 +192,17 @@ const FieldStatsComponent: React.FC = ({
abortControllerRef.current?.abort();
abortControllerRef.current = new AbortController();
- const results = isTextBased
- ? await loadFieldStatsTextBased({
- services: { data },
- dataView: loadedDataView,
- field,
- fromDate,
- toDate,
- baseQuery: query,
- abortController: abortControllerRef.current,
- })
- : await loadFieldStats({
- services: { data },
- dataView: loadedDataView,
- field,
- fromDate,
- toDate,
- dslQuery:
- dslQuery ??
- buildEsQuery(
- loadedDataView,
- query ?? [],
- filters ?? [],
- getEsQueryConfig(uiSettings)
- ),
- abortController: abortControllerRef.current,
- });
+ const results = await loadFieldStats({
+ services: { data },
+ dataView: loadedDataView,
+ field,
+ fromDate,
+ toDate,
+ dslQuery:
+ dslQuery ??
+ buildEsQuery(loadedDataView, query ?? [], filters ?? [], getEsQueryConfig(uiSettings)),
+ abortController: abortControllerRef.current,
+ });
abortControllerRef.current = null;
@@ -297,7 +287,7 @@ const FieldStatsComponent: React.FC = ({
let title = <>>;
function combineWithTitleAndFooter(el: React.ReactElement) {
- const countsElement = getCountsElement(state, services, isTextBased, dataTestSubject);
+ const countsElement = getCountsElement(state, services, isEsqlQuery, dataTestSubject);
return (
<>
@@ -319,7 +309,7 @@ const FieldStatsComponent: React.FC = ({
);
}
- if (!canProvideStatsForField(field, isTextBased)) {
+ if (!canProvideStatsForField(field, isEsqlQuery)) {
const messageNoAnalysis = (
= ({
: messageNoAnalysis;
}
- if (canProvideNumberSummaryForField(field, isTextBased) && isNumberSummaryValid(numberSummary)) {
+ if (canProvideNumberSummaryForField(field, isEsqlQuery) && isNumberSummaryValid(numberSummary)) {
title = (
@@ -563,21 +553,19 @@ const FieldStatsComponent: React.FC = ({
function getCountsElement(
state: FieldStatsState,
services: FieldStatsServices,
- isTextBased: boolean,
+ isEsqlQuery: boolean,
dataTestSubject: string
): JSX.Element {
const dataTestSubjDocsCount = 'unifiedFieldStats-statsFooter-docsCount';
const { fieldFormats } = services;
- const { totalDocuments, sampledValues, sampledDocuments, topValues } = state;
+ const { totalDocuments, sampledDocuments } = state;
- if (!totalDocuments) {
+ if (!totalDocuments || isEsqlQuery) {
return <>>;
}
- let labelElement;
-
- if (isTextBased) {
- labelElement = topValues?.areExamples ? (
+ const labelElement =
+ sampledDocuments && sampledDocuments < totalDocuments ? (
) : (
{fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
- .convert(sampledValues)}
+ .convert(totalDocuments)}
),
}}
/>
);
- } else {
- labelElement =
- sampledDocuments && sampledDocuments < totalDocuments ? (
-
- {fieldFormats
- .getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
- .convert(sampledDocuments)}
-
- ),
- }}
- />
- ) : (
-
- {fieldFormats
- .getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
- .convert(totalDocuments)}
-
- ),
- }}
- />
- );
- }
return (
diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx
index 7864976c1180f..b139e7b5685c5 100644
--- a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx
+++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx
@@ -32,7 +32,7 @@ import type {
UnifiedFieldListSidebarContainerStateService,
AddFieldFilterHandler,
} from '../../types';
-import { canProvideStatsForFieldTextBased } from '../../utils/can_provide_stats';
+import { canProvideStatsForEsqlField } from '../../utils/can_provide_stats';
interface GetCommonFieldItemButtonPropsParams {
stateService: UnifiedFieldListSidebarContainerStateService;
@@ -405,7 +405,7 @@ function UnifiedFieldListItemComponent({
/>
)}
renderContent={
- (searchMode === 'text-based' && canProvideStatsForFieldTextBased(field)) ||
+ (searchMode === 'text-based' && canProvideStatsForEsqlField(field)) ||
searchMode === 'documents'
? renderPopover
: undefined
diff --git a/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.test.ts b/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.test.ts
index 6e14c16f7e42c..7e77bd7852726 100644
--- a/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.test.ts
+++ b/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.test.ts
@@ -223,7 +223,7 @@ describe('fieldExamplesCalculator', function () {
values: getFieldValues(hits, dataView.fields.getByName('extension')!, dataView),
field: dataView.fields.getByName('extension')!,
count: 3,
- isTextBased: false,
+ isEsqlQuery: false,
};
});
@@ -286,33 +286,19 @@ describe('fieldExamplesCalculator', function () {
expect(getFieldExampleBuckets(params).sampledValues).toBe(5);
});
- it('works for text-based', function () {
- const result = getFieldExampleBuckets({
- values: [['a'], ['b'], ['a'], ['a']],
- field: { name: 'message', type: 'string', esTypes: ['text'] } as DataViewField,
- isTextBased: true,
- });
- expect(result).toMatchInlineSnapshot(`
- Object {
- "buckets": Array [
- Object {
- "count": 3,
- "key": "a",
- },
- Object {
- "count": 1,
- "key": "b",
- },
- ],
- "sampledDocuments": 4,
- "sampledValues": 4,
- }
- `);
+ it('should not work for ES|QL', function () {
+ expect(() =>
+ getFieldExampleBuckets({
+ values: [['a'], ['b'], ['a'], ['a']],
+ field: { name: 'message', type: 'string', esTypes: ['text'] } as DataViewField,
+ isEsqlQuery: true,
+ })
+ ).toThrowError();
expect(() =>
getFieldExampleBuckets({
values: [['a'], ['b'], ['a'], ['a']],
field: { name: 'message', type: 'string', esTypes: ['keyword'] } as DataViewField,
- isTextBased: true,
+ isEsqlQuery: true,
})
).toThrowError();
});
diff --git a/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.ts b/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.ts
index e4413f3be7fe2..55d0c30b58e34 100644
--- a/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.ts
+++ b/packages/kbn-unified-field-list/src/services/field_examples_calculator/field_examples_calculator.ts
@@ -23,7 +23,7 @@ export interface FieldValueCountsParams {
values: FieldHitValue[];
field: DataViewField;
count?: number;
- isTextBased: boolean;
+ isEsqlQuery: boolean;
}
export function getFieldExampleBuckets(params: FieldValueCountsParams, formatter?: FieldFormat) {
@@ -31,7 +31,7 @@ export function getFieldExampleBuckets(params: FieldValueCountsParams, formatter
count: DEFAULT_SIMPLE_EXAMPLES_SIZE,
});
- if (!canProvideExamplesForField(params.field, params.isTextBased)) {
+ if (!canProvideExamplesForField(params.field, params.isEsqlQuery)) {
throw new Error(
`Analysis is not available this field type: "${params.field.type}". Field name: "${params.field.name}"`
);
diff --git a/packages/kbn-unified-field-list/src/services/field_stats/field_stats_utils.ts b/packages/kbn-unified-field-list/src/services/field_stats/field_stats_utils.ts
index 309f5f054683b..57a7d0be8fda9 100644
--- a/packages/kbn-unified-field-list/src/services/field_stats/field_stats_utils.ts
+++ b/packages/kbn-unified-field-list/src/services/field_stats/field_stats_utils.ts
@@ -416,7 +416,7 @@ export async function getSimpleExamples(
values: getFieldValues(simpleExamplesResult.hits.hits, field, dataView),
field,
count: DEFAULT_SIMPLE_EXAMPLES_SIZE,
- isTextBased: false,
+ isEsqlQuery: false,
},
formatter
);
diff --git a/packages/kbn-unified-field-list/src/services/field_stats_text_based/field_stats_utils_text_based.test.ts b/packages/kbn-unified-field-list/src/services/field_stats_text_based/field_stats_utils_text_based.test.ts
deleted file mode 100644
index 553fdd749941f..0000000000000
--- a/packages/kbn-unified-field-list/src/services/field_stats_text_based/field_stats_utils_text_based.test.ts
+++ /dev/null
@@ -1,129 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
- * License v3.0 only", or the "Server Side Public License, v 1".
- */
-
-import type { DataViewField } from '@kbn/data-views-plugin/common';
-import { buildSearchFilter, fetchAndCalculateFieldStats } from './field_stats_utils_text_based';
-
-describe('fieldStatsUtilsTextBased', function () {
- describe('buildSearchFilter()', () => {
- it('should create a time range filter', () => {
- expect(
- buildSearchFilter({
- timeFieldName: 'timestamp',
- fromDate: '2022-12-05T23:00:00.000Z',
- toDate: '2023-01-05T09:33:05.359Z',
- })
- ).toMatchInlineSnapshot(`
- Object {
- "range": Object {
- "timestamp": Object {
- "format": "strict_date_optional_time",
- "gte": "2022-12-05T23:00:00.000Z",
- "lte": "2023-01-05T09:33:05.359Z",
- },
- },
- }
- `);
- });
- it('should not create a time range filter', () => {
- expect(
- buildSearchFilter({
- timeFieldName: undefined,
- fromDate: '2022-12-05T23:00:00.000Z',
- toDate: '2023-01-05T09:33:05.359Z',
- })
- ).toBeNull();
- });
- });
-
- describe('fetchAndCalculateFieldStats()', () => {
- it('should provide top values', async () => {
- const searchHandler = jest.fn().mockResolvedValue({
- values: [
- [3, 'a'],
- [1, 'b'],
- ],
- });
- expect(
- await fetchAndCalculateFieldStats({
- searchHandler,
- esqlBaseQuery: 'from logs* | limit 1000',
- field: { name: 'message', type: 'string', esTypes: ['keyword'] } as DataViewField,
- })
- ).toMatchInlineSnapshot(`
- Object {
- "sampledDocuments": 4,
- "sampledValues": 4,
- "topValues": Object {
- "buckets": Array [
- Object {
- "count": 3,
- "key": "a",
- },
- Object {
- "count": 1,
- "key": "b",
- },
- ],
- },
- "totalDocuments": 4,
- }
- `);
- expect(searchHandler).toHaveBeenCalledWith(
- expect.objectContaining({
- query:
- 'from logs* | limit 1000\n| WHERE `message` IS NOT NULL\n | STATS `message_terms` = count(`message`) BY `message`\n | SORT `message_terms` DESC\n | LIMIT 10',
- })
- );
- });
-
- it('should provide text examples', async () => {
- const searchHandler = jest.fn().mockResolvedValue({
- values: [[['programming', 'cool']], ['elastic', 'cool']],
- });
- expect(
- await fetchAndCalculateFieldStats({
- searchHandler,
- esqlBaseQuery: 'from logs* | limit 1000',
- field: { name: 'message', type: 'string', esTypes: ['text'] } as DataViewField,
- })
- ).toMatchInlineSnapshot(`
- Object {
- "sampledDocuments": 2,
- "sampledValues": 4,
- "topValues": Object {
- "areExamples": true,
- "buckets": Array [
- Object {
- "count": 2,
- "key": "cool",
- },
- Object {
- "count": 1,
- "key": "elastic",
- },
- Object {
- "count": 1,
- "key": "programming",
- },
- ],
- },
- "totalDocuments": 2,
- }
- `);
-
- expect(searchHandler).toHaveBeenCalledWith(
- expect.objectContaining({
- query:
- 'from logs* | limit 1000\n| WHERE `message` IS NOT NULL\n | KEEP `message`\n | LIMIT 100',
- })
- );
- });
- });
-});
diff --git a/packages/kbn-unified-field-list/src/services/field_stats_text_based/field_stats_utils_text_based.ts b/packages/kbn-unified-field-list/src/services/field_stats_text_based/field_stats_utils_text_based.ts
deleted file mode 100644
index b64d26b0cbb59..0000000000000
--- a/packages/kbn-unified-field-list/src/services/field_stats_text_based/field_stats_utils_text_based.ts
+++ /dev/null
@@ -1,156 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
- * License v3.0 only", or the "Server Side Public License, v 1".
- */
-
-import type { ESQLSearchResponse } from '@kbn/es-types';
-import { appendToESQLQuery } from '@kbn/esql-utils';
-import type { DataViewField } from '@kbn/data-views-plugin/common';
-import type { FieldStatsResponse } from '../../types';
-import {
- DEFAULT_TOP_VALUES_SIZE,
- DEFAULT_SIMPLE_EXAMPLES_SIZE,
- SIMPLE_EXAMPLES_FETCH_SIZE,
-} from '../../constants';
-import {
- canProvideStatsForFieldTextBased,
- canProvideTopValuesForFieldTextBased,
- canProvideExamplesForField,
-} from '../../utils/can_provide_stats';
-import { getFieldExampleBuckets } from '../field_examples_calculator';
-
-export type SearchHandlerTextBased = ({ query }: { query: string }) => Promise;
-
-export function buildSearchFilter({
- timeFieldName,
- fromDate,
- toDate,
-}: {
- timeFieldName?: string;
- fromDate: string;
- toDate: string;
-}) {
- return timeFieldName
- ? {
- range: {
- [timeFieldName]: {
- gte: fromDate,
- lte: toDate,
- format: 'strict_date_optional_time',
- },
- },
- }
- : null;
-}
-
-interface FetchAndCalculateFieldStatsParams {
- searchHandler: SearchHandlerTextBased;
- field: DataViewField;
- esqlBaseQuery: string;
-}
-
-export async function fetchAndCalculateFieldStats(params: FetchAndCalculateFieldStatsParams) {
- const { field } = params;
- if (!canProvideStatsForFieldTextBased(field)) {
- return {};
- }
- if (field.type === 'boolean') {
- return await getStringTopValues(params, 3);
- }
- if (canProvideTopValuesForFieldTextBased(field)) {
- return await getStringTopValues(params);
- }
- if (canProvideExamplesForField(field, true)) {
- return await getSimpleTextExamples(params);
- }
-
- return {};
-}
-
-export async function getStringTopValues(
- params: FetchAndCalculateFieldStatsParams,
- size = DEFAULT_TOP_VALUES_SIZE
-): Promise> {
- const { searchHandler, field, esqlBaseQuery } = params;
- const safeEsqlFieldName = getSafeESQLFieldName(field.name);
- const safeEsqlFieldNameTerms = getSafeESQLFieldName(`${field.name}_terms`);
- const esqlQuery = appendToESQLQuery(
- esqlBaseQuery,
- `| WHERE ${safeEsqlFieldName} IS NOT NULL
- | STATS ${safeEsqlFieldNameTerms} = count(${safeEsqlFieldName}) BY ${safeEsqlFieldName}
- | SORT ${safeEsqlFieldNameTerms} DESC
- | LIMIT ${size}`
- );
-
- const result = await searchHandler({ query: esqlQuery });
- const values = result?.values as Array<[number, string]>;
-
- if (!values?.length) {
- return {};
- }
-
- const sampledValues = values?.reduce((acc: number, row) => acc + row[0], 0);
-
- const topValues = {
- buckets: values.map((value) => ({
- count: value[0],
- key: value[1],
- })),
- };
-
- return {
- totalDocuments: sampledValues,
- sampledDocuments: sampledValues,
- sampledValues,
- topValues,
- };
-}
-
-export async function getSimpleTextExamples(
- params: FetchAndCalculateFieldStatsParams
-): Promise> {
- const { searchHandler, field, esqlBaseQuery } = params;
- const safeEsqlFieldName = getSafeESQLFieldName(field.name);
- const esqlQuery = appendToESQLQuery(
- esqlBaseQuery,
- `| WHERE ${safeEsqlFieldName} IS NOT NULL
- | KEEP ${safeEsqlFieldName}
- | LIMIT ${SIMPLE_EXAMPLES_FETCH_SIZE}`
- );
-
- const result = await searchHandler({ query: esqlQuery });
- const values = ((result?.values as Array<[string | string[]]>) || []).map((value) =>
- Array.isArray(value) && value.length === 1 ? value[0] : value
- );
-
- if (!values?.length) {
- return {};
- }
-
- const sampledDocuments = values?.length;
-
- const fieldExampleBuckets = getFieldExampleBuckets({
- values,
- field,
- count: DEFAULT_SIMPLE_EXAMPLES_SIZE,
- isTextBased: true,
- });
-
- return {
- totalDocuments: sampledDocuments,
- sampledDocuments: fieldExampleBuckets.sampledDocuments,
- sampledValues: fieldExampleBuckets.sampledValues,
- topValues: {
- buckets: fieldExampleBuckets.buckets,
- areExamples: true,
- },
- };
-}
-
-function getSafeESQLFieldName(str: string): string {
- return `\`${str}\``;
-}
diff --git a/packages/kbn-unified-field-list/src/services/field_stats_text_based/index.ts b/packages/kbn-unified-field-list/src/services/field_stats_text_based/index.ts
deleted file mode 100644
index 8915a30bf4f41..0000000000000
--- a/packages/kbn-unified-field-list/src/services/field_stats_text_based/index.ts
+++ /dev/null
@@ -1,10 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
- * License v3.0 only", or the "Server Side Public License, v 1".
- */
-
-export { loadFieldStatsTextBased } from './load_field_stats_text_based';
diff --git a/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts b/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts
deleted file mode 100644
index 5f77f15906896..0000000000000
--- a/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts
+++ /dev/null
@@ -1,89 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
- * License v3.0 only", or the "Server Side Public License, v 1".
- */
-
-import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
-import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
-import type { AggregateQuery } from '@kbn/es-query';
-import { getESQLWithSafeLimit, getESQLResults } from '@kbn/esql-utils';
-import type { FieldStatsResponse } from '../../types';
-import {
- buildSearchFilter,
- SearchHandlerTextBased,
- fetchAndCalculateFieldStats,
-} from './field_stats_utils_text_based';
-import { ESQL_SAFE_LIMIT } from '../../constants';
-
-interface FetchFieldStatsParamsTextBased {
- services: {
- data: DataPublicPluginStart;
- };
- dataView: DataView;
- field: DataViewField;
- fromDate: string;
- toDate: string;
- baseQuery: AggregateQuery;
- abortController?: AbortController;
-}
-
-export type LoadFieldStatsTextBasedHandler = (
- params: FetchFieldStatsParamsTextBased
-) => Promise>;
-
-/**
- * Loads and aggregates stats data for an ES|QL query field
- * @param services
- * @param dataView
- * @param field
- * @param fromDate
- * @param toDate
- * @param baseQuery
- * @param abortController
- */
-export const loadFieldStatsTextBased: LoadFieldStatsTextBasedHandler = async ({
- services,
- dataView,
- field,
- fromDate,
- toDate,
- baseQuery,
- abortController,
-}) => {
- const { data } = services;
-
- try {
- if (!dataView?.id || !field?.type) {
- return {};
- }
-
- const searchHandler: SearchHandlerTextBased = async ({ query }) => {
- const filter = buildSearchFilter({ timeFieldName: dataView.timeFieldName, fromDate, toDate });
- const result = await getESQLResults({
- esqlQuery: query,
- filter,
- search: data.search.search,
- signal: abortController?.signal,
- timeRange: { from: fromDate, to: toDate },
- });
- return result.response;
- };
-
- if (!('esql' in baseQuery)) {
- throw new Error('query must be of type AggregateQuery');
- }
-
- return await fetchAndCalculateFieldStats({
- searchHandler,
- field,
- esqlBaseQuery: getESQLWithSafeLimit(baseQuery.esql, ESQL_SAFE_LIMIT),
- });
- } catch (error) {
- // console.error(error);
- throw new Error('Could not provide field stats', { cause: error });
- }
-};
diff --git a/packages/kbn-unified-field-list/src/utils/can_provide_stats.test.ts b/packages/kbn-unified-field-list/src/utils/can_provide_stats.test.ts
index 297e1e26c8c56..c27a44494a3e7 100644
--- a/packages/kbn-unified-field-list/src/utils/can_provide_stats.test.ts
+++ b/packages/kbn-unified-field-list/src/utils/can_provide_stats.test.ts
@@ -10,7 +10,7 @@
import {
canProvideStatsForField,
canProvideExamplesForField,
- canProvideStatsForFieldTextBased,
+ canProvideStatsForEsqlField,
} from './can_provide_stats';
import type { DataViewField } from '@kbn/data-views-plugin/common';
import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub';
@@ -34,40 +34,12 @@ describe('can_provide_stats', function () {
);
});
- it('works for text based columns', function () {
+ it('should not work for ES|QL columns', function () {
expect(
canProvideStatsForField(
{ name: 'message', type: 'string', esTypes: ['text'] } as DataViewField,
true
)
- ).toBe(true);
- expect(
- canProvideStatsForField(
- { name: 'message', type: 'string', esTypes: ['keyword'] } as DataViewField,
- true
- )
- ).toBe(true);
- expect(
- canProvideStatsForField({ name: 'message', type: 'number' } as DataViewField, true)
- ).toBe(true);
- expect(
- canProvideStatsForField({ name: 'message', type: 'boolean' } as DataViewField, true)
- ).toBe(true);
- expect(canProvideStatsForField({ name: 'message', type: 'ip' } as DataViewField, true)).toBe(
- true
- );
- expect(
- canProvideStatsForField({ name: 'message', type: 'geo_point' } as DataViewField, true)
- ).toBe(true);
- expect(
- canProvideStatsForField(
- { name: '_id', type: 'string', esTypes: ['keyword'] } as DataViewField,
- true
- )
- ).toBe(true);
-
- expect(
- canProvideStatsForField({ name: 'message', type: 'date' } as DataViewField, true)
).toBe(false);
});
});
@@ -82,83 +54,24 @@ describe('can_provide_stats', function () {
);
});
- it('works for text based columns', function () {
+ it('should not work for ES|QL columns', function () {
expect(
canProvideExamplesForField(
{ name: 'message', type: 'string', esTypes: ['text'] } as DataViewField,
true
)
- ).toBe(true);
- expect(
- canProvideExamplesForField(
- { name: 'message', type: 'string', esTypes: ['keyword'] } as DataViewField,
- true
- )
- ).toBe(false);
- expect(
- canProvideExamplesForField({ name: 'message', type: 'number' } as DataViewField, true)
- ).toBe(false);
- expect(
- canProvideExamplesForField({ name: 'message', type: 'boolean' } as DataViewField, true)
).toBe(false);
- expect(
- canProvideExamplesForField({ name: 'message', type: 'ip' } as DataViewField, true)
- ).toBe(false);
- expect(
- canProvideExamplesForField({ name: 'message', type: 'geo_point' } as DataViewField, true)
- ).toBe(true);
- expect(
- canProvideExamplesForField({ name: 'message', type: 'date' } as DataViewField, true)
- ).toBe(false);
- expect(
- canProvideStatsForField(
- { name: '_id', type: 'string', esTypes: ['keyword'] } as DataViewField,
- true
- )
- ).toBe(true);
});
- describe('canProvideStatsForFieldTextBased', function () {
- it('works for text based columns', function () {
+ describe('canProvideStatsForEsqlField', function () {
+ it('should not work for ES|QL columns', function () {
expect(
- canProvideStatsForFieldTextBased({
+ canProvideStatsForEsqlField({
name: 'message',
type: 'string',
esTypes: ['text'],
} as DataViewField)
- ).toBe(true);
- expect(
- canProvideStatsForFieldTextBased({
- name: 'message',
- type: 'string',
- esTypes: ['keyword'],
- } as DataViewField)
- ).toBe(true);
- expect(
- canProvideStatsForFieldTextBased({ name: 'message', type: 'number' } as DataViewField)
- ).toBe(true);
- expect(
- canProvideStatsForFieldTextBased({ name: 'message', type: 'boolean' } as DataViewField)
- ).toBe(true);
- expect(
- canProvideStatsForFieldTextBased({ name: 'message', type: 'ip' } as DataViewField)
- ).toBe(true);
- expect(
- canProvideStatsForFieldTextBased({ name: 'message', type: 'ip_range' } as DataViewField)
).toBe(false);
- expect(
- canProvideStatsForFieldTextBased({ name: 'message', type: 'geo_point' } as DataViewField)
- ).toBe(true);
- expect(
- canProvideStatsForFieldTextBased({ name: 'message', type: 'date' } as DataViewField)
- ).toBe(false);
- expect(
- canProvideStatsForFieldTextBased({
- name: '_id',
- type: 'string',
- esTypes: ['keyword'],
- } as DataViewField)
- ).toBe(true);
});
});
});
diff --git a/packages/kbn-unified-field-list/src/utils/can_provide_stats.ts b/packages/kbn-unified-field-list/src/utils/can_provide_stats.ts
index e84137fa17f2e..c3fd9734de5e3 100644
--- a/packages/kbn-unified-field-list/src/utils/can_provide_stats.ts
+++ b/packages/kbn-unified-field-list/src/utils/can_provide_stats.ts
@@ -9,22 +9,22 @@
import type { DataViewField } from '@kbn/data-views-plugin/common';
-export function canProvideStatsForField(field: DataViewField, isTextBased: boolean): boolean {
- if (isTextBased) {
- return canProvideStatsForFieldTextBased(field);
+export function canProvideStatsForField(field: DataViewField, isEsqlQuery: boolean): boolean {
+ if (isEsqlQuery) {
+ return false;
}
return (
- (field.aggregatable && canProvideAggregatedStatsForField(field, isTextBased)) ||
+ (field.aggregatable && canProvideAggregatedStatsForField(field, isEsqlQuery)) ||
((!field.aggregatable || field.type === 'geo_point' || field.type === 'geo_shape') &&
- canProvideExamplesForField(field, isTextBased))
+ canProvideExamplesForField(field, isEsqlQuery))
);
}
export function canProvideAggregatedStatsForField(
field: DataViewField,
- isTextBased: boolean
+ isEsqlQuery: boolean
): boolean {
- if (isTextBased) {
+ if (isEsqlQuery) {
return false;
}
return !(
@@ -39,20 +39,17 @@ export function canProvideAggregatedStatsForField(
export function canProvideNumberSummaryForField(
field: DataViewField,
- isTextBased: boolean
+ isEsqlQuery: boolean
): boolean {
- if (isTextBased) {
+ if (isEsqlQuery) {
return false;
}
return field.timeSeriesMetric === 'counter';
}
-export function canProvideExamplesForField(field: DataViewField, isTextBased: boolean): boolean {
- if (isTextBased) {
- return (
- (field.type === 'string' && !canProvideTopValuesForFieldTextBased(field)) ||
- ['geo_point', 'geo_shape'].includes(field.type)
- );
+export function canProvideExamplesForField(field: DataViewField, isEsqlQuery: boolean): boolean {
+ if (isEsqlQuery) {
+ return false;
}
if (field.name === '_score') {
return false;
@@ -69,17 +66,6 @@ export function canProvideExamplesForField(field: DataViewField, isTextBased: bo
].includes(field.type);
}
-export function canProvideTopValuesForFieldTextBased(field: DataViewField): boolean {
- if (field.name === '_id') {
- return false;
- }
- const esTypes = field.esTypes?.[0];
- return (
- Boolean(field.type === 'string' && esTypes && ['keyword', 'version'].includes(esTypes)) ||
- ['keyword', 'version', 'ip', 'number', 'boolean'].includes(field.type)
- );
-}
-
-export function canProvideStatsForFieldTextBased(field: DataViewField): boolean {
- return canProvideTopValuesForFieldTextBased(field) || canProvideExamplesForField(field, true);
+export function canProvideStatsForEsqlField(field: DataViewField): boolean {
+ return false;
}
diff --git a/packages/kbn-unified-field-list/tsconfig.json b/packages/kbn-unified-field-list/tsconfig.json
index 830e56ac6ab00..54b67143b7c7b 100644
--- a/packages/kbn-unified-field-list/tsconfig.json
+++ b/packages/kbn-unified-field-list/tsconfig.json
@@ -32,7 +32,6 @@
"@kbn/shared-ux-button-toolbar",
"@kbn/field-utils",
"@kbn/visualization-utils",
- "@kbn/esql-utils",
"@kbn/search-types",
"@kbn/fields-metadata-plugin",
"@kbn/ui-theme"
diff --git a/test/functional/apps/discover/group6/_sidebar_field_stats.ts b/test/functional/apps/discover/group6/_sidebar_field_stats.ts
index 325adb313ed6c..e725606609996 100644
--- a/test/functional/apps/discover/group6/_sidebar_field_stats.ts
+++ b/test/functional/apps/discover/group6/_sidebar_field_stats.ts
@@ -155,77 +155,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await unifiedFieldList.waitUntilSidebarHasLoaded();
});
- it('should show top values popover for numeric field', async () => {
+ it('should not show top values popover for numeric field', async () => {
await unifiedFieldList.clickFieldListItem('bytes');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Top values');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(10);
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '42 sample values'
- );
-
- await unifiedFieldList.clickFieldListPlusFilter('bytes', '0');
- const editorValue = await monacoEditor.getCodeEditorValue();
- expect(editorValue).to.eql(
- `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`bytes\`==0`
- );
+ await testSubjects.missingOrFail('dscFieldStats-statsFooter');
await unifiedFieldList.closeFieldPopover();
});
- it('should show a top values popover for a keyword field', async () => {
+ it('should not show a top values popover for a keyword field', async () => {
await unifiedFieldList.clickFieldListItem('extension.raw');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Top values');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(5);
- await testSubjects.missingOrFail('unifiedFieldStats-buttonGroup');
- await testSubjects.missingOrFail('unifiedFieldStats-histogram');
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '500 sample values'
- );
-
- await unifiedFieldList.clickFieldListPlusFilter('extension.raw', 'css');
- const editorValue = await monacoEditor.getCodeEditorValue();
- expect(editorValue).to.eql(
- `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`extension.raw\`=="css"`
- );
-
- await unifiedFieldList.closeFieldPopover();
- });
-
- it('should show a top values popover for an ip field', async () => {
- await unifiedFieldList.clickFieldListItem('clientip');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Top values');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(10);
- await testSubjects.missingOrFail('unifiedFieldStats-buttonGroup');
- await testSubjects.missingOrFail('unifiedFieldStats-histogram');
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '32 sample values'
- );
-
- await unifiedFieldList.clickFieldListPlusFilter('clientip', '216.126.255.31');
- const editorValue = await monacoEditor.getCodeEditorValue();
- expect(editorValue).to.eql(
- `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`clientip\`::string=="216.126.255.31"`
- );
-
- await unifiedFieldList.closeFieldPopover();
- });
-
- it('should show a top values popover for _index field', async () => {
- await unifiedFieldList.clickFieldListItem('_index');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Top values');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(1);
- await testSubjects.missingOrFail('unifiedFieldStats-buttonGroup');
- await testSubjects.missingOrFail('unifiedFieldStats-histogram');
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '500 sample values'
- );
+ await testSubjects.missingOrFail('dscFieldStats-statsFooter');
await unifiedFieldList.closeFieldPopover();
});
@@ -240,102 +178,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await unifiedFieldList.closeFieldPopover();
});
- it('should show examples for geo points field', async () => {
- await unifiedFieldList.clickFieldListItem('geo.coordinates');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Examples');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(11);
- await testSubjects.missingOrFail('unifiedFieldStats-buttonGroup');
- await testSubjects.missingOrFail('unifiedFieldStats-histogram');
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '100 sample records'
- );
- await unifiedFieldList.closeFieldPopover();
- });
-
- it('should show examples for text field', async () => {
+ it('should not show examples for text field', async () => {
await unifiedFieldList.clickFieldListItem('extension');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Examples');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(5);
- await testSubjects.missingOrFail('unifiedFieldStats-buttonGroup');
- await testSubjects.missingOrFail('unifiedFieldStats-histogram');
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '100 sample records'
- );
-
- await unifiedFieldList.clickFieldListPlusFilter('extension', 'css');
- const editorValue = await monacoEditor.getCodeEditorValue();
- expect(editorValue).to.eql(
- `from logstash-* METADATA _index, _id | sort @timestamp desc | limit 500\n| WHERE \`extension\`=="css"`
- );
-
- await unifiedFieldList.closeFieldPopover();
- });
-
- it('should show examples for _id field', async () => {
- await unifiedFieldList.clickFieldListItem('_id');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Examples');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(11);
- await testSubjects.missingOrFail('unifiedFieldStats-buttonGroup');
- await testSubjects.missingOrFail('unifiedFieldStats-histogram');
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '100 sample records'
- );
- await unifiedFieldList.closeFieldPopover();
- });
-
- it('should show a top values popover for a more complex query', async () => {
- const testQuery = `from logstash-* | sort @timestamp desc | limit 50 | stats avg(bytes) by geo.dest | limit 3`;
- await monacoEditor.setCodeEditorValue(testQuery);
- await testSubjects.click('querySubmitButton');
- await header.waitUntilLoadingHasFinished();
- await unifiedFieldList.waitUntilSidebarHasLoaded();
-
- await unifiedFieldList.clickFieldListItem('avg(bytes)');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Top values');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(3);
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '3 sample values'
- );
-
- await unifiedFieldList.clickFieldListPlusFilter('avg(bytes)', '5453');
- const editorValue = await monacoEditor.getCodeEditorValue();
- expect(editorValue).to.eql(
- `from logstash-* | sort @timestamp desc | limit 50 | stats avg(bytes) by geo.dest | limit 3\n| WHERE \`avg(bytes)\`==5453`
- );
-
- await unifiedFieldList.closeFieldPopover();
- });
-
- it('should show a top values popover for a boolean field', async () => {
- const testQuery = `row enabled = true`;
- await monacoEditor.setCodeEditorValue(testQuery);
- await testSubjects.click('querySubmitButton');
- await header.waitUntilLoadingHasFinished();
- await unifiedFieldList.waitUntilSidebarHasLoaded();
-
- await unifiedFieldList.clickFieldListItem('enabled');
- await testSubjects.existOrFail('dscFieldStats-topValues');
- expect(await testSubjects.getVisibleText('dscFieldStats-title')).to.be('Top values');
- const topValuesRows = await testSubjects.findAll('dscFieldStats-topValues-bucket');
- expect(topValuesRows.length).to.eql(1);
- expect(await unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
- 'true\n100%'
- );
- expect(await testSubjects.getVisibleText('dscFieldStats-statsFooter')).to.contain(
- '1 sample value'
- );
-
- await unifiedFieldList.clickFieldListMinusFilter('enabled', 'true');
- const editorValue = await monacoEditor.getCodeEditorValue();
- expect(editorValue).to.eql(`row enabled = true\n| WHERE \`enabled\`!=true`);
+ await testSubjects.missingOrFail('dscFieldStats-statsFooter');
await unifiedFieldList.closeFieldPopover();
});
});
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 0f5c34aaae9f6..e3201bc94972f 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -8455,7 +8455,6 @@
"unifiedFieldList.fieldsAccordion.existenceTimeoutLabel": "Les informations de champ ont pris trop de temps",
"unifiedFieldList.fieldStats.bucketPercentageTooltip": "{formattedPercentage} ({count, plural, one {# enregistrement} other {# enregistrements}})",
"unifiedFieldList.fieldStats.calculatedFromSampleRecordsLabel": "Calculé à partir de {sampledDocumentsFormatted} {sampledDocuments, plural, one {exemple d'enregistrement} other {exemples d'enregistrement}}.",
- "unifiedFieldList.fieldStats.calculatedFromSampleValuesLabel": "Calculé à partir {sampledValuesFormatted} {sampledValues, plural, one {d'un exemple de valeur} other {d’exemples de valeur}}.",
"unifiedFieldList.fieldStats.calculatedFromTotalRecordsLabel": "Calculé à partir de {totalDocumentsFormatted} {totalDocuments, plural, one {enregistrement} other {enregistrements}}.",
"unifiedFieldList.fieldStats.countLabel": "Décompte",
"unifiedFieldList.fieldStats.displayToggleLegend": "Basculer soit",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 28840b8c7765c..7f32d93fc486b 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -8444,7 +8444,6 @@
"unifiedFieldList.fieldsAccordion.existenceTimeoutLabel": "フィールド情報に時間がかかりすぎました",
"unifiedFieldList.fieldStats.bucketPercentageTooltip": "{formattedPercentage} ({count, plural, other {# レコード}})",
"unifiedFieldList.fieldStats.calculatedFromSampleRecordsLabel": "{sampledDocumentsFormatted}サンプル{sampledDocuments, plural, other {レコード}}から計算されました。",
- "unifiedFieldList.fieldStats.calculatedFromSampleValuesLabel": "{sampledValuesFormatted}サンプル{sampledValues, plural, other {値}}から計算されました。",
"unifiedFieldList.fieldStats.calculatedFromTotalRecordsLabel": "{totalDocumentsFormatted} {totalDocuments, plural, other {レコード}}から計算されました。",
"unifiedFieldList.fieldStats.countLabel": "カウント",
"unifiedFieldList.fieldStats.displayToggleLegend": "次のどちらかを切り替えます:",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 918a37764c448..f6a0af9af8771 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -8279,7 +8279,6 @@
"unifiedFieldList.fieldsAccordion.existenceTimeoutLabel": "字段信息花费时间过久",
"unifiedFieldList.fieldStats.bucketPercentageTooltip": "{formattedPercentage}({count, plural, other {# 条记录}})",
"unifiedFieldList.fieldStats.calculatedFromSampleRecordsLabel": "基于 {sampledDocumentsFormatted} 个样例{sampledDocuments, plural, other {记录}}计算。",
- "unifiedFieldList.fieldStats.calculatedFromSampleValuesLabel": "基于 {sampledValuesFormatted} 个样例{sampledValues, plural, other {值}}计算。",
"unifiedFieldList.fieldStats.calculatedFromTotalRecordsLabel": "基于 {totalDocumentsFormatted} 个样例{totalDocuments, plural, other {记录}}计算。",
"unifiedFieldList.fieldStats.countLabel": "计数",
"unifiedFieldList.fieldStats.displayToggleLegend": "切换",