Skip to content

Commit ded68e7

Browse files
committed
unlocks aggregation suggestions in STATS autocomplete
1 parent fc5c3b8 commit ded68e7

2 files changed

Lines changed: 45 additions & 7 deletions

File tree

src/platform/packages/shared/kbn-esql-language/src/commands/registry/stats/autocomplete.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,28 @@ describe('STATS Autocomplete', () => {
420420
await statsExpectSuggestions('from a | stats a=min(b), b=max(', expected, mockCallbacks);
421421
});
422422

423+
test('inside a function arg with hint.kind === "aggregation" (e.g. SPARKLINE first arg), only aggregation functions are suggested', async () => {
424+
// Mock fields just to verify the resolver's exclusive path skips the composite (fields) branch
425+
const allFields = getFieldNamesByType('any');
426+
(mockCallbacks.getByType as jest.Mock).mockResolvedValue(
427+
allFields.map((name) => ({ label: name, text: name }))
428+
);
429+
430+
const expectedAggregations = getFunctionSignaturesByReturnType(
431+
Location.STATS,
432+
['integer', 'long', 'double'],
433+
{ agg: true, timeseriesAgg: true },
434+
undefined,
435+
['sparkline']
436+
).map((s) => `${s},`);
437+
438+
await statsExpectSuggestions(
439+
'from a | stats SPARKLINE(',
440+
expectedAggregations,
441+
mockCallbacks
442+
);
443+
});
444+
423445
test('inside function argument list', async () => {
424446
const expectedFieldsAvg = getFieldNamesByType(AVG_TYPES);
425447
(mockCallbacks.getByType as jest.Mock).mockResolvedValue(

src/platform/packages/shared/kbn-esql-language/src/commands/registry/stats/autocomplete.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,14 +416,30 @@ function buildCustomFilteringContext(
416416

417417
if (!isInBy) {
418418
statsSpecificFunctionsToIgnore.push(
419-
...getFunctionsToIgnoreForStats(command, finalCommandArgIndex),
420-
...(isAggFunctionUsedAlready(command, finalCommandArgIndex)
421-
? getAllFunctions({ type: FunctionDefinitionTypes.AGG }).map(({ name }) => name)
422-
: []),
423-
...(isTimeseriesAggUsedAlready(command, finalCommandArgIndex)
424-
? getAllFunctions({ type: FunctionDefinitionTypes.TIME_SERIES_AGG }).map(({ name }) => name)
425-
: [])
419+
...getFunctionsToIgnoreForStats(command, finalCommandArgIndex)
426420
);
421+
422+
// The "no nested aggregations" rule applies unless the cursor's param
423+
// explicitly expects an aggregation (e.g. SPARKLINE's first arg has
424+
// hint.kind === 'aggregation'), in which case nesting is required, not forbidden.
425+
const expectsAggregation = basicContext.paramDefinitions.some(
426+
(p) => p.hint?.kind === 'aggregation'
427+
);
428+
429+
if (!expectsAggregation) {
430+
if (isAggFunctionUsedAlready(command, finalCommandArgIndex)) {
431+
statsSpecificFunctionsToIgnore.push(
432+
...getAllFunctions({ type: FunctionDefinitionTypes.AGG }).map(({ name }) => name)
433+
);
434+
}
435+
if (isTimeseriesAggUsedAlready(command, finalCommandArgIndex)) {
436+
statsSpecificFunctionsToIgnore.push(
437+
...getAllFunctions({ type: FunctionDefinitionTypes.TIME_SERIES_AGG }).map(
438+
({ name }) => name
439+
)
440+
);
441+
}
442+
}
427443
}
428444

429445
return {

0 commit comments

Comments
 (0)