From 1b94087b42a18ca1136dd9775f16506db4817c69 Mon Sep 17 00:00:00 2001 From: Eyo Okon Eyo Date: Wed, 18 Feb 2026 20:29:29 +0100 Subject: [PATCH 1/2] add consideration for inline cast to cascade experience capture rules --- .../cascaded_documents_helpers/index.test.ts | 24 +++++++++++++++++++ .../utils/cascaded_documents_helpers/index.ts | 11 ++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.test.ts b/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.test.ts index 3922e55c5523d..b3dc1683a24f3 100644 --- a/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.test.ts +++ b/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.test.ts @@ -185,6 +185,30 @@ describe('cascaded documents helpers utils', () => { expect(result.appliedFunctions).toEqual([]); }); + it('should return empty metadata when a group field is an inline cast wrapping an unsupported function', () => { + const queryString = `FROM kibana_sample_data_logs | STATS count = COUNT(*) BY ts = BUCKET(@timestamp, 1 hour)::string`; + + const result = getESQLStatsQueryMeta(queryString); + expect(result.groupByFields).toEqual([]); + expect(result.appliedFunctions).toEqual([]); + }); + + it('should return empty metadata when a group field among multiple is an inline cast wrapping an unsupported function', () => { + const queryString = `FROM kibana_sample_data_logs | STATS count = COUNT(*) BY agent.keyword, ts = BUCKET(@timestamp, 1 hour)::string`; + + const result = getESQLStatsQueryMeta(queryString); + expect(result.groupByFields).toEqual([]); + expect(result.appliedFunctions).toEqual([]); + }); + + it('should allow an inline cast of a simple column', () => { + const queryString = `FROM kibana_sample_data_logs | STATS count = COUNT(*) BY casted = clientip::keyword`; + + const result = getESQLStatsQueryMeta(queryString); + expect(result.groupByFields).toEqual([{ field: 'casted', type: 'inlineCast' }]); + expect(result.appliedFunctions).toEqual([{ identifier: 'count', aggregation: 'COUNT' }]); + }); + it('should return the appropriate metadata despite there being a keep, as long as it specifies the current group field', () => { const queryString = ` FROM kibana_sample_data_logs | STATS Visits = COUNT(), Unique = COUNT_DISTINCT(clientip), p95 = PERCENTILE(bytes, 95), median = MEDIAN(bytes) BY response.keyword | KEEP Visits, Unique, p95, median, response.keyword | LIMIT 123 diff --git a/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts b/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts index f98737c2ddc0e..6cb5de94c30f9 100644 --- a/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts +++ b/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts @@ -23,6 +23,7 @@ import { type ESQLColumn, isBinaryExpression, Walker, + isInlineCast, } from '@kbn/esql-language'; import type { BinaryExpressionComparisonOperator, @@ -141,10 +142,14 @@ export const getESQLStatsQueryMeta = (queryString: string): ESQLStatsQueryMeta = } const groupFieldDefinition = getFieldDefinitionFromArg(groupFieldNode.arg); + if ( - isFunctionExpression(groupFieldDefinition) && - (!isSupportedStatsFunction(groupFieldDefinition.name) || - isCategorizeFunctionWithFunctionArgument(groupFieldDefinition)) + (isFunctionExpression(groupFieldDefinition) && + (!isSupportedStatsFunction(groupFieldDefinition.name) || + isCategorizeFunctionWithFunctionArgument(groupFieldDefinition))) || + (isInlineCast(groupFieldDefinition) && + isFunctionExpression(groupFieldDefinition.value) && + !isSupportedStatsFunction(groupFieldDefinition.value.name)) ) { // if the group field has a grouping function that is not supported, // this nullifies the entire query to count as a valid query for the cascade experience From e6382f0539594f6304af2bc13cfd110dac76556f Mon Sep 17 00:00:00 2001 From: Stratoula Date: Thu, 19 Feb 2026 08:39:06 +0100 Subject: [PATCH 2/2] Small improvement for readability --- .../utils/cascaded_documents_helpers/index.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts b/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts index 6cb5de94c30f9..f89574c10295f 100644 --- a/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts +++ b/src/platform/packages/shared/kbn-esql-utils/src/utils/cascaded_documents_helpers/index.ts @@ -30,6 +30,7 @@ import type { ESQLBinaryExpression, ESQLUnaryExpression, ESQLPostfixUnaryExpression, + ESQLProperNode, } from '@kbn/esql-language/src/types'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { ESQLControlVariable } from '@kbn/esql-types'; @@ -54,6 +55,22 @@ import { isCategorizeFunctionWithFunctionArgument, } from './utils'; +const hasUnsupportedGroupingFunction = (definition: ESQLProperNode): boolean => { + const funcExpr = isFunctionExpression(definition) + ? definition + : isInlineCast(definition) && isFunctionExpression(definition.value) + ? definition.value + : null; + + if (!funcExpr) { + return false; + } + + return ( + !isSupportedStatsFunction(funcExpr.name) || isCategorizeFunctionWithFunctionArgument(funcExpr) + ); +}; + type NodeType = 'group' | 'leaf'; export interface AppliedStatsFunction { @@ -143,14 +160,7 @@ export const getESQLStatsQueryMeta = (queryString: string): ESQLStatsQueryMeta = const groupFieldDefinition = getFieldDefinitionFromArg(groupFieldNode.arg); - if ( - (isFunctionExpression(groupFieldDefinition) && - (!isSupportedStatsFunction(groupFieldDefinition.name) || - isCategorizeFunctionWithFunctionArgument(groupFieldDefinition))) || - (isInlineCast(groupFieldDefinition) && - isFunctionExpression(groupFieldDefinition.value) && - !isSupportedStatsFunction(groupFieldDefinition.value.name)) - ) { + if (hasUnsupportedGroupingFunction(groupFieldDefinition)) { // if the group field has a grouping function that is not supported, // this nullifies the entire query to count as a valid query for the cascade experience groupByFields.splice(0, groupByFields.length);