Skip to content

Commit d216d84

Browse files
qn895stratoulaelasticmachine
committed
[ES|QL] Add automated script to sync operators and add support for MATCH operators (elastic#205565)
Closes elastic#188978 Closes elastic#199092 This PR addresses elastic#199092 and adds automated script to sync operators and adds support for MATCH and its equivalent `:` match operator. https://github.com/user-attachments/assets/8f78345d-f1a0-4be4-8eba-2aae35225842 https://github.com/user-attachments/assets/eb9e63e3-0086-45a5-b782-199225895afc ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --------- Co-authored-by: Stratoula Kalafateli <[email protected]> Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit 6fed083)
1 parent d852ec2 commit d216d84

File tree

16 files changed

+5697
-331
lines changed

16 files changed

+5697
-331
lines changed

src/platform/packages/shared/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts

Lines changed: 415 additions & 7 deletions
Large diffs are not rendered by default.

src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.where.test.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ describe('WHERE <expression>', () => {
7474
const expectedComparisonWithDateSuggestions = [
7575
...getDateLiterals(),
7676
...getFieldNamesByType(['date']),
77-
// all functions compatible with a keywordField type
78-
...getFunctionSignaturesByReturnType('where', ['date'], { scalar: true }),
77+
...getFieldNamesByType(['date_nanos']),
78+
...getFunctionSignaturesByReturnType('where', ['date', 'date_nanos'], { scalar: true }),
7979
];
8080
await assertSuggestions(
8181
'from a | where dateField == /',
@@ -213,10 +213,18 @@ describe('WHERE <expression>', () => {
213213
]);
214214
await assertSuggestions('from index | WHERE not /', [
215215
...getFieldNamesByType('boolean').map((name) => attachTriggerCommand(`${name} `)),
216-
...getFunctionSignaturesByReturnType('where', 'boolean', { scalar: true }),
216+
...getFunctionSignaturesByReturnType('where', 'boolean', { scalar: true }, undefined, [
217+
':',
218+
]),
217219
]);
218220
await assertSuggestions('FROM index | WHERE NOT ENDS_WITH(keywordField, "foo") /', [
219-
...getFunctionSignaturesByReturnType('where', 'boolean', { builtin: true }, ['boolean']),
221+
...getFunctionSignaturesByReturnType(
222+
'where',
223+
'boolean',
224+
{ builtin: true },
225+
['boolean'],
226+
[':']
227+
),
220228
pipeCompleteItem,
221229
]);
222230
await assertSuggestions('from index | WHERE keywordField IS NOT/', [
@@ -291,9 +299,13 @@ describe('WHERE <expression>', () => {
291299
const { assertSuggestions } = await setup();
292300

293301
await assertSuggestions('FROM index | WHERE doubleField + doubleField /', [
294-
...getFunctionSignaturesByReturnType('where', 'any', { builtin: true, skipAssign: true }, [
295-
'double',
296-
]),
302+
...getFunctionSignaturesByReturnType(
303+
'where',
304+
'any',
305+
{ builtin: true, skipAssign: true },
306+
['double'],
307+
[':']
308+
),
297309
]);
298310
});
299311

src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ import {
8181
getOperatorSuggestions,
8282
getSuggestionsAfterNot,
8383
} from './factories';
84-
import { EDITOR_MARKER, METADATA_FIELDS } from '../shared/constants';
84+
import { EDITOR_MARKER, FULL_TEXT_SEARCH_FUNCTIONS, METADATA_FIELDS } from '../shared/constants';
8585
import { getAstContext, removeMarkerArgFromArgsList } from '../shared/context';
8686
import {
8787
buildQueryUntilPreviousCommand,
@@ -1229,10 +1229,12 @@ async function getFunctionArgsSuggestions(
12291229
}))
12301230
);
12311231
}
1232+
12321233
// could also be in stats (bucket) but our autocomplete is not great yet
12331234
if (
12341235
(getTypesFromParamDefs(typesToSuggestNext).includes('date') &&
1235-
['where', 'eval'].includes(command.name)) ||
1236+
['where', 'eval'].includes(command.name) &&
1237+
!FULL_TEXT_SEARCH_FUNCTIONS.includes(fnDefinition.name)) ||
12361238
(command.name === 'stats' &&
12371239
typesToSuggestNext.some((t) => t && t.type === 'date' && t.constantOnly === true))
12381240
)

src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/where/index.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { getOverlapRange, getSuggestionsToRightOfOperatorExpression } from '../.
2929
import { getPosition } from './util';
3030
import { pipeCompleteItem } from '../../complete_items';
3131
import {
32+
EDITOR_MARKER,
3233
UNSUPPORTED_COMMANDS_BEFORE_MATCH,
3334
UNSUPPORTED_COMMANDS_BEFORE_QSTR,
3435
} from '../../../shared/constants';
@@ -169,12 +170,14 @@ export async function suggest(
169170
if (priorCommands.some((c) => UNSUPPORTED_COMMANDS_BEFORE_QSTR.has(c))) {
170171
ignored.push('qstr');
171172
}
172-
173-
const columnSuggestions = await getColumnsByType('any', [], {
174-
advanceCursor: true,
175-
openSuggestions: true,
176-
});
177-
173+
const last = fullTextAst?.[fullTextAst.length - 1];
174+
let columnSuggestions: SuggestionRawDefinition[] = [];
175+
if (!last?.text?.endsWith(`:${EDITOR_MARKER}`)) {
176+
columnSuggestions = await getColumnsByType('any', [], {
177+
advanceCursor: true,
178+
openSuggestions: true,
179+
});
180+
}
178181
suggestions.push(
179182
...columnSuggestions,
180183
...getFunctionSuggestions({ command: 'where', ignored })

src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ export async function getSuggestionsToRightOfOperatorExpression({
575575
operatorReturnType === 'unknown' || operatorReturnType === 'unsupported'
576576
? 'any'
577577
: operatorReturnType,
578-
ignored: ['='],
578+
ignored: ['=', ':'],
579579
})
580580
);
581581
} else {

src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/builtin.ts

Lines changed: 3 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
*/
99

1010
import { i18n } from '@kbn/i18n';
11-
import { ESQL_NUMBER_TYPES, isNumericType } from '../shared/esql_types';
11+
import { isNumericType } from '../shared/esql_types';
1212
import type { FunctionDefinition, FunctionParameterType, FunctionReturnType } from './types';
13-
13+
import { operatorsFunctionDefinitions } from './generated/operators';
1414
type MathFunctionSignature = [FunctionParameterType, FunctionParameterType, FunctionReturnType];
1515

1616
function createMathDefinition(
@@ -384,150 +384,6 @@ export const comparisonFunctions: FunctionDefinition[] = [
384384
},
385385
].map((op): FunctionDefinition => createComparisonDefinition(op));
386386

387-
const likeFunctions: FunctionDefinition[] = [
388-
// Skip the insensitive case equality until it gets restored back
389-
// new special comparison operator for strings only
390-
// {
391-
// name: '=~',
392-
// description: i18n.translate('kbn-esql-validation-autocomplete.esql.definition.equalToCaseInsensitiveDoc', {
393-
// defaultMessage: 'Case insensitive equality',
394-
// }),
395-
// },
396-
{
397-
name: 'like',
398-
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definition.likeDoc', {
399-
defaultMessage: 'Filter data based on string patterns',
400-
}),
401-
},
402-
{ name: 'not_like', description: '' },
403-
{
404-
name: 'rlike',
405-
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definition.rlikeDoc', {
406-
defaultMessage: 'Filter data based on string regular expressions',
407-
}),
408-
},
409-
{ name: 'not_rlike', description: '' },
410-
].map(({ name, description }) => {
411-
const def: FunctionDefinition = {
412-
type: 'builtin' as const,
413-
ignoreAsSuggestion: /not/.test(name),
414-
name,
415-
description,
416-
supportedCommands: ['eval', 'where', 'row', 'sort'],
417-
supportedOptions: ['by'],
418-
signatures: [
419-
{
420-
params: [
421-
{ name: 'left', type: 'text' as const },
422-
{ name: 'right', type: 'text' as const },
423-
],
424-
returnType: 'boolean',
425-
},
426-
{
427-
params: [
428-
{ name: 'left', type: 'text' as const },
429-
{ name: 'right', type: 'keyword' as const },
430-
],
431-
returnType: 'boolean',
432-
},
433-
{
434-
params: [
435-
{ name: 'left', type: 'keyword' as const },
436-
{ name: 'right', type: 'text' as const },
437-
],
438-
returnType: 'boolean',
439-
},
440-
{
441-
params: [
442-
{ name: 'left', type: 'keyword' as const },
443-
{ name: 'right', type: 'keyword' as const },
444-
],
445-
returnType: 'boolean',
446-
},
447-
],
448-
};
449-
450-
return def;
451-
});
452-
453-
const inFunctions: FunctionDefinition[] = [
454-
{
455-
name: 'in',
456-
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definition.inDoc', {
457-
defaultMessage:
458-
'Tests if the value an expression takes is contained in a list of other expressions',
459-
}),
460-
},
461-
{ name: 'not_in', description: '' },
462-
].map<FunctionDefinition>(({ name, description }) => ({
463-
// set all arrays to type "any" for now
464-
// this only applies to the "in" operator
465-
// e.g. "foo" in ( "foo", "bar" )
466-
//
467-
// we did this because the "in" operator now supports
468-
// mixed-type arrays like ( "1.2.3", versionVar )
469-
// because of implicit casting.
470-
//
471-
// we need to revisit with more robust validation
472-
type: 'builtin',
473-
ignoreAsSuggestion: /not/.test(name),
474-
name,
475-
description,
476-
supportedCommands: ['eval', 'where', 'row', 'sort'],
477-
signatures: [
478-
...ESQL_NUMBER_TYPES.map((type) => ({
479-
params: [
480-
{ name: 'left', type: type as FunctionParameterType },
481-
482-
{ name: 'right', type: 'any[]' as FunctionParameterType },
483-
],
484-
returnType: 'boolean' as FunctionReturnType,
485-
})),
486-
{
487-
params: [
488-
{ name: 'left', type: 'keyword' },
489-
{ name: 'right', type: 'any[]' },
490-
],
491-
returnType: 'boolean',
492-
},
493-
{
494-
params: [
495-
{ name: 'left', type: 'text' },
496-
{ name: 'right', type: 'any[]' },
497-
],
498-
returnType: 'boolean',
499-
},
500-
{
501-
params: [
502-
{ name: 'left', type: 'boolean' },
503-
{ name: 'right', type: 'any[]' },
504-
],
505-
returnType: 'boolean',
506-
},
507-
{
508-
params: [
509-
{ name: 'left', type: 'date' },
510-
{ name: 'right', type: 'any[]' },
511-
],
512-
returnType: 'boolean',
513-
},
514-
{
515-
params: [
516-
{ name: 'left', type: 'version' },
517-
{ name: 'right', type: 'any[]' },
518-
],
519-
returnType: 'boolean',
520-
},
521-
{
522-
params: [
523-
{ name: 'left', type: 'ip' },
524-
{ name: 'right', type: 'any[]' },
525-
],
526-
returnType: 'boolean',
527-
},
528-
],
529-
}));
530-
531387
export const logicalOperators: FunctionDefinition[] = [
532388
{
533389
name: 'and',
@@ -681,10 +537,7 @@ const otherDefinitions: FunctionDefinition[] = [
681537
];
682538

683539
export const builtinFunctions: FunctionDefinition[] = [
684-
...mathFunctions,
685-
...comparisonFunctions,
686-
...likeFunctions,
687-
...inFunctions,
540+
...operatorsFunctionDefinitions,
688541
...logicalOperators,
689542
...nullFunctions,
690543
...otherDefinitions,

0 commit comments

Comments
 (0)