Skip to content

Commit

Permalink
[ES|QL] Support params in column nodes and function names (elastic#19…
Browse files Browse the repository at this point in the history
…8957)

## Summary

Closes elastic#198251

Adds support for the new ES|QL param syntax in the following places:

- Support all param types in `column` AST node types (single level and
nested)
- Support those new language features in:
   - AST
   - Parser
   - Traversal: `Walker`
- Support in validation and autocomplete:
  - Makes tests pass
  - Make validation and autocomplete not complain on param usage in
    - Function names
    - Function arguments
    - In function names where aggregation functions expected
    - Column names
      - First level
      - Nested
      - Exception out of "unknownField" errors


### Checklist

Delete any items that are not applicable to this PR.

- [x] [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


### For maintainers

- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)

---------

Co-authored-by: Stratoula Kalafateli <[email protected]>
  • Loading branch information
vadimkibana and stratoula authored Nov 7, 2024
1 parent 9353497 commit b0af8af
Show file tree
Hide file tree
Showing 22 changed files with 912 additions and 187 deletions.
14 changes: 11 additions & 3 deletions packages/kbn-esql-ast/src/builder/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

/* eslint-disable @typescript-eslint/no-namespace */

import { LeafPrinter } from '../pretty_print';
import {
ESQLAstComment,
ESQLAstCommentMultiLine,
Expand Down Expand Up @@ -125,16 +126,23 @@ export namespace Builder {
};

export const column = (
template: Omit<AstNodeTemplate<ESQLColumn>, 'name' | 'quoted'>,
template: Omit<AstNodeTemplate<ESQLColumn>, 'name' | 'quoted' | 'parts'>,
fromParser?: Partial<AstNodeParserFields>
): ESQLColumn => {
return {
const node: ESQLColumn = {
...template,
...Builder.parserFields(fromParser),
parts: template.args.map((arg) =>
arg.type === 'identifier' ? arg.name : LeafPrinter.param(arg)
),
quoted: false,
name: template.parts.join('.'),
name: '',
type: 'column',
};

node.name = LeafPrinter.column(node);

return node;
};

export const order = (
Expand Down
37 changes: 31 additions & 6 deletions packages/kbn-esql-ast/src/mutate/commands/from/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ describe('commands.from.metadata', () => {

expect(column).toMatchObject({
type: 'column',
parts: ['a'],
args: [
{
type: 'identifier',
name: 'a',
},
],
// parts: ['a'],
});
});

Expand All @@ -40,19 +46,39 @@ describe('commands.from.metadata', () => {
expect(columns).toMatchObject([
{
type: 'column',
parts: ['a'],
args: [
{
type: 'identifier',
name: 'a',
},
],
},
{
type: 'column',
parts: ['b'],
args: [
{
type: 'identifier',
name: 'b',
},
],
},
{
type: 'column',
parts: ['_id'],
args: [
{
type: 'identifier',
name: '_id',
},
],
},
{
type: 'column',
parts: ['_lang'],
args: [
{
type: 'identifier',
name: '_lang',
},
],
},
]);
});
Expand Down Expand Up @@ -156,7 +182,6 @@ describe('commands.from.metadata', () => {
it('return inserted `column` node, and parent `option` node', () => {
const src1 = 'FROM index METADATA a';
const { root } = parse(src1);

const tuple = commands.from.metadata.insert(root, 'b');

expect(tuple).toMatchObject([
Expand Down
22 changes: 18 additions & 4 deletions packages/kbn-esql-ast/src/mutate/commands/from/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export const find = (
}

const predicate: Predicate<[ESQLColumn, unknown]> = ([field]) =>
cmpArr(field.parts, fieldName as string[]);
cmpArr(
field.args.map((arg) => (arg.type === 'identifier' ? arg.name : '')),
fieldName as string[]
);

return findByPredicate(list(ast), predicate);
};
Expand Down Expand Up @@ -128,7 +131,12 @@ export const remove = (
fieldName = [fieldName];
}

return removeByPredicate(ast, (field) => cmpArr(field.parts, fieldName as string[]));
return removeByPredicate(ast, (field) =>
cmpArr(
field.args.map((arg) => (arg.type === 'identifier' ? arg.name : '')),
fieldName as string[]
)
);
};

/**
Expand Down Expand Up @@ -161,7 +169,8 @@ export const insert = (
}

const parts: string[] = typeof fieldName === 'string' ? [fieldName] : fieldName;
const column = Builder.expression.column({ parts });
const args = parts.map((part) => Builder.identifier({ name: part }));
const column = Builder.expression.column({ args });

if (index === -1) {
option.args.push(column);
Expand Down Expand Up @@ -195,7 +204,12 @@ export const upsert = (
const parts = Array.isArray(fieldName) ? fieldName : [fieldName];
const existing = Walker.find(
option,
(node) => node.type === 'column' && cmpArr(node.parts, parts)
(node) =>
node.type === 'column' &&
cmpArr(
node.args.map((arg) => (arg.type === 'identifier' ? arg.name : '')),
parts
)
);
if (existing) {
return undefined;
Expand Down
4 changes: 2 additions & 2 deletions packages/kbn-esql-ast/src/mutate/commands/sort/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,13 @@ describe('commands.sort', () => {
args: [
{
type: 'column',
parts: ['b', 'a'],
args: [{ name: 'b' }, { name: 'a' }],
},
],
});
expect(node2).toMatchObject({
type: 'column',
parts: ['a', 'b'],
args: [{ name: 'a' }, { name: 'b' }],
});
});

Expand Down
30 changes: 19 additions & 11 deletions packages/kbn-esql-ast/src/mutate/commands/sort/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,17 @@ export type NewSortExpressionTemplate =
const createSortExpression = (
template: string | string[] | NewSortExpressionTemplate
): SortExpression => {
const parts: string[] =
typeof template === 'string'
? [template]
: Array.isArray(template)
? template
: typeof template.parts === 'string'
? [template.parts]
: template.parts;
const identifiers = parts.map((part) => Builder.identifier({ name: part }));
const column = Builder.expression.column({
parts:
typeof template === 'string'
? [template]
: Array.isArray(template)
? template
: typeof template.parts === 'string'
? [template.parts]
: template.parts,
args: identifiers,
});

if (typeof template === 'string' || Array.isArray(template)) {
Expand Down Expand Up @@ -189,12 +191,18 @@ export const find = (
return findByPredicate(ast, ([node]) => {
let isMatch = false;
if (node.type === 'column') {
isMatch = util.cmpArr(node.parts, arrParts);
isMatch = util.cmpArr(
node.args.map((arg) => (arg.type === 'identifier' ? arg.name : '')),
arrParts
);
} else if (node.type === 'order') {
const columnParts = (node.args[0] as ESQLColumn)?.parts;
const columnParts = (node.args[0] as ESQLColumn)?.args;

if (Array.isArray(columnParts)) {
isMatch = util.cmpArr(columnParts, arrParts);
isMatch = util.cmpArr(
columnParts.map((arg) => (arg.type === 'identifier' ? arg.name : '')),
arrParts
);
}
}

Expand Down
Loading

0 comments on commit b0af8af

Please sign in to comment.