Skip to content

Commit bde7aaf

Browse files
committed
support row and ts in from e where in subqueries
1 parent 8a9a290 commit bde7aaf

8 files changed

Lines changed: 65 additions & 35 deletions

File tree

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -375,18 +375,14 @@ function buildSubqueryCompleteItem(sourceCommand: string): ISuggestionItem {
375375
});
376376
}
377377

378-
export function buildSubqueryCompleteItems(sourceCommands?: string[]): ISuggestionItem[] {
379-
const allowedSourceCommands = sourceCommands
380-
? new Set(sourceCommands.map((command) => command.toLowerCase()))
381-
: undefined;
382-
378+
export function buildSubqueryCompleteItems(): ISuggestionItem[] {
383379
return esqlCommandRegistry
384380
.getAllCommands()
385381
.filter(
386-
({ name, metadata }) =>
382+
({ metadata }) =>
387383
metadata.subquerySource === true &&
388384
metadata.hidden !== true &&
389-
(!allowedSourceCommands || allowedSourceCommands.has(name))
385+
!metadata.subquerySourceHidden
390386
)
391387
.map(({ name }) => buildSubqueryCompleteItem(name));
392388
}

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

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const fromExpectSuggestions = (
3737
);
3838
};
3939

40+
const subquerySuggestions = ['(FROM $0)'];
41+
4042
const visibleIndices =
4143
mockContext.sources?.filter((source) => !source.hidden).map((source) => source.name) || [];
4244

@@ -92,8 +94,16 @@ describe('FROM Autocomplete', () => {
9294
});
9395

9496
test('suggests visible indices on space', async () => {
95-
await fromExpectSuggestions('from /', [...visibleIndices, '(FROM $0)'], mockCallbacks);
96-
await fromExpectSuggestions('FROM /', [...visibleIndices, '(FROM $0)'], mockCallbacks);
97+
await fromExpectSuggestions(
98+
'from /',
99+
[...visibleIndices, ...subquerySuggestions],
100+
mockCallbacks
101+
);
102+
await fromExpectSuggestions(
103+
'FROM /',
104+
[...visibleIndices, ...subquerySuggestions],
105+
mockCallbacks
106+
);
97107
await fromExpectSuggestions('from /index', visibleIndices, mockCallbacks);
98108
});
99109

@@ -102,13 +112,17 @@ describe('FROM Autocomplete', () => {
102112
});
103113

104114
test('does create suggestions after a closed quote', async () => {
105-
await fromExpectSuggestions('FROM "lolz", ', [...visibleIndices, '(FROM $0)'], mockCallbacks);
115+
await fromExpectSuggestions(
116+
'FROM "lolz", ',
117+
[...visibleIndices, ...subquerySuggestions],
118+
mockCallbacks
119+
);
106120
});
107121

108122
test('doesnt suggest indices twice', async () => {
109123
await fromExpectSuggestions(
110124
'from index, ',
111-
[...visibleIndices.filter((i) => i !== 'index'), '(FROM $0)'],
125+
[...visibleIndices.filter((i) => i !== 'index'), ...subquerySuggestions],
112126
mockCallbacks
113127
);
114128
});
@@ -134,16 +148,24 @@ describe('FROM Autocomplete', () => {
134148
const expectedSuggestions = visibleDataSources.map((source) => source.name);
135149
mockContext.sources = visibleDataSources;
136150

137-
await fromExpectSuggestions('from ', [...expectedSuggestions, '(FROM $0)'], mockCallbacks);
138-
await fromExpectSuggestions('FROM ', [...expectedSuggestions, '(FROM $0)'], mockCallbacks);
151+
await fromExpectSuggestions(
152+
'from ',
153+
[...expectedSuggestions, ...subquerySuggestions],
154+
mockCallbacks
155+
);
156+
await fromExpectSuggestions(
157+
'FROM ',
158+
[...expectedSuggestions, ...subquerySuggestions],
159+
mockCallbacks
160+
);
139161
await fromExpectSuggestions(
140162
'FROM a,/',
141163
expectedSuggestions.filter((i) => i !== 'a'),
142164
mockCallbacks
143165
);
144166
await fromExpectSuggestions(
145167
'from a, /',
146-
[...expectedSuggestions.filter((i) => i !== 'a'), '(FROM $0)'],
168+
[...expectedSuggestions.filter((i) => i !== 'a'), ...subquerySuggestions],
147169
mockCallbacks
148170
);
149171
await fromExpectSuggestions('from *,/', expectedSuggestions, mockCallbacks);
@@ -161,7 +183,7 @@ describe('FROM Autocomplete', () => {
161183
const expectedFromViews = ['my_saved_view', 'my-view'];
162184
await fromExpectSuggestions(
163185
'from ',
164-
[...expectedFromSources, ...expectedFromViews, '(FROM $0)'],
186+
[...expectedFromSources, ...expectedFromViews, ...subquerySuggestions],
165187
mockCallbacks,
166188
contextWithViews
167189
);
@@ -192,7 +214,7 @@ describe('FROM Autocomplete', () => {
192214

193215
await fromExpectSuggestions(
194216
'from ',
195-
[...expectedFromSources, 'my_saved_view', 'my_dataset', '(FROM $0)'],
217+
[...expectedFromSources, 'my_saved_view', 'my_dataset', ...subquerySuggestions],
196218
mockCallbacks,
197219
contextWithDatasets
198220
);
@@ -294,13 +316,17 @@ describe('FROM Autocomplete', () => {
294316
].sort();
295317

296318
test('suggests subquery on space after FROM', async () => {
297-
await fromExpectSuggestions('from /', [...visibleIndices, '(FROM $0)'], mockCallbacks);
319+
await fromExpectSuggestions(
320+
'from /',
321+
[...visibleIndices, ...subquerySuggestions],
322+
mockCallbacks
323+
);
298324
});
299325

300326
test('suggests subquery after comma', async () => {
301327
await fromExpectSuggestions(
302328
'from index, /',
303-
[...visibleIndices.filter((i) => i !== 'index'), '(FROM $0)'],
329+
[...visibleIndices.filter((i) => i !== 'index'), ...subquerySuggestions],
304330
mockCallbacks
305331
);
306332
});

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,8 @@ async function handleFromAutocomplete(
7171
// Use commandText for pattern matching (e.g., /METADATA\s+$/ and trailing whitespace)
7272
// checks need to operate on the current command only, not the entire query
7373
const commandText = query.substring(command.location.min, cursorPos);
74-
const subquerySuggestion =
75-
commandText.length > command.name.length
76-
? buildSubqueryCompleteItems([command.name]).at(0)
77-
: undefined;
74+
const subquerySuggestions =
75+
commandText.length > command.name.length ? buildSubqueryCompleteItems() : undefined;
7876
const indicesBrowserSuggestion = await getIndicesBrowserSuggestion({
7977
callbacks,
8078
context,
@@ -99,7 +97,7 @@ async function handleFromAutocomplete(
9997
context,
10098
innerText,
10199
command.location.min,
102-
subquerySuggestion
100+
subquerySuggestions
103101
);
104102
if (indicesBrowserSuggestion) {
105103
suggestions.unshift(indicesBrowserSuggestion);
@@ -122,7 +120,7 @@ async function handleFromAutocomplete(
122120
context,
123121
callbacks,
124122
indexes,
125-
subquerySuggestion,
123+
subquerySuggestions,
126124
{
127125
textBeforeCursor: innerText,
128126
commandStart: command.location.min, // Full-query offset of this FROM command.
@@ -141,7 +139,7 @@ function suggestInitialSources(
141139
context: ICommandContext | undefined,
142140
innerText: string,
143141
commandStart: number,
144-
subquerySuggestion?: ISuggestionItem
142+
subquerySuggestions?: ISuggestionItem[]
145143
): ISuggestionItem[] {
146144
let sources = context?.sources ?? [];
147145

@@ -157,8 +155,8 @@ function suggestInitialSources(
157155
const datasetSuggestions = buildDatasetsDefinitions(context?.datasets ?? [], []);
158156
const suggestions = [...sourceSuggestions, ...viewSuggestions, ...datasetSuggestions];
159157

160-
if (subquerySuggestion && shouldSuggestSubquery(context)) {
161-
suggestions.push(subquerySuggestion);
158+
if (subquerySuggestions && shouldSuggestSubquery(context)) {
159+
suggestions.push(...subquerySuggestions);
162160
}
163161

164162
return suggestions;
@@ -189,7 +187,7 @@ async function suggestAdditionalSources(
189187
context: ICommandContext | undefined,
190188
callbacks: ICommandCallbacks | undefined,
191189
indexes: ReturnType<typeof getSourcesFromCommands>,
192-
subquerySuggestion?: ISuggestionItem,
190+
subquerySuggestions?: ISuggestionItem[],
193191
sourceReplacementContext?: {
194192
textBeforeCursor: string;
195193
commandStart: number;
@@ -227,8 +225,8 @@ async function suggestAdditionalSources(
227225
canRewriteFromToTs ? sourceReplacementContext : undefined
228226
);
229227

230-
if (subquerySuggestion && isRestartingExpression(innerText) && shouldSuggestSubquery(context)) {
231-
suggestions.push(subquerySuggestion);
228+
if (subquerySuggestions && isRestartingExpression(innerText) && shouldSuggestSubquery(context)) {
229+
suggestions.push(...subquerySuggestions);
232230
}
233231

234232
return suggestions;

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,16 @@ describe('CommandRegistry', () => {
261261
expect(commandNames).toContain('hiddenOutsideCommand');
262262
});
263263

264-
test('should only return FROM when starting a subquery', () => {
265-
const fromCommand = createMockCommand('from');
264+
test('should only return subquery source commands when starting a subquery', () => {
265+
const fromCommand = createMockCommand('from', undefined, { subquerySource: true });
266+
const rowCommand = createMockCommand('row', undefined, { subquerySource: true, subquerySourceHidden: true });
267+
const tsCommand = createMockCommand('ts', undefined, { subquerySource: true, subquerySourceHidden: true });
266268
const evalCommand = createMockCommand('eval');
267269
const whereCommand = createMockCommand('where');
268270

269271
registry.registerCommand(fromCommand);
272+
registry.registerCommand(rowCommand);
273+
registry.registerCommand(tsCommand);
270274
registry.registerCommand(evalCommand);
271275
registry.registerCommand(whereCommand);
272276

@@ -276,6 +280,10 @@ describe('CommandRegistry', () => {
276280

277281
const commandNames = commands.map((cmd) => cmd.name);
278282
expect(commandNames).toEqual(['from']);
283+
expect(commandNames).not.toContain('row');
284+
expect(commandNames).not.toContain('ts');
285+
expect(commandNames).not.toContain('eval');
286+
expect(commandNames).not.toContain('where');
279287
});
280288
});
281289

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export interface ICommandMetadata {
9999
hiddenAfterCommands?: string[]; // Optional list of command names; this command is not suggested when any of them appear anywhere in the pipeline
100100
subquerySupport?: boolean; // Temporary property to indicate if the command supports subquery suggestions.
101101
subquerySource?: boolean; // Optional property to indicate if the command can start a subquery expression.
102+
subquerySourceHidden?: boolean; // Hides the command from subquery source suggestions (e.g. snapshot-only commands).
102103
subqueryRestrictions?: {
103104
hideInside: boolean; // Command is hidden inside subqueries
104105
hideOutside: boolean; // Command is hidden outside subqueries (at root level)
@@ -282,7 +283,7 @@ export class CommandRegistry implements ICommandRegistry {
282283
const queryContainsSubqueries = options?.queryContainsSubqueries ?? false;
283284

284285
const filtered = isStartingSubquery
285-
? allCommands.filter(({ name }) => name === 'from')
286+
? allCommands.filter(({ metadata }) => !!metadata.subquerySource && !metadata.subquerySourceHidden)
286287
: allCommands;
287288

288289
// Then apply subquery restrictions

src/platform/packages/shared/kbn-esql-language/src/commands/registry/row/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const rowCommand = {
2727
metadata: {
2828
type: 'source' as const,
2929
subquerySource: true,
30+
subquerySourceHidden: true,
3031
description: i18n.translate('kbn-esql-language.esql.definitions.rowDoc', {
3132
defaultMessage:
3233
'Produces a row with one or more columns with values that you specify. This can be useful for testing.',

src/platform/packages/shared/kbn-esql-language/src/commands/registry/timeseries/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const timeseriesCommand = {
2626
methods: timeseriesCommandMethods,
2727
metadata: {
2828
type: 'source' as const,
29+
subquerySource: true,
30+
subquerySourceHidden: true,
2931
hidden: false,
3032
preview: true,
3133
isTimeseries: true,

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,10 @@ describe('WHERE Autocomplete', () => {
305305
await whereExpectSuggestions('from index | WHERE doubleField in ', [
306306
'($0)',
307307
'(FROM $0)',
308-
'(ROW $0)',
309308
]);
310309
await whereExpectSuggestions('from index | WHERE doubleField not in ', [
311310
'($0)',
312311
'(FROM $0)',
313-
'(ROW $0)',
314312
]);
315313

316314
await whereExpectSuggestions(

0 commit comments

Comments
 (0)