-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ES|QL] AST node synthesizer #201814
[ES|QL] AST node synthesizer #201814
Conversation
Pinging @elastic/kibana-esql (Team:ESQL) |
Head branch was pushed to by a user without write access
💚 Build Succeeded
Metrics [docs]Page load bundle
History
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is lovely! LGTM
Starting backport for target branches: 8.x https://github.com/elastic/kibana/actions/runs/12116127168 |
## Summary Closes elastic#191812 Adds ability to create ES|QL AST nodes from plain strings and compose them. Create an integer literal: ```js const node = expr `42`; ``` Create any expression: ```js const node = expr `nested.field = fn(123)`; ``` Compose AST nodes: ```js const value = expr `123`; const node = expr `nested.field = fn(${value})`; ``` ## Usage You can create an assignment expression AST node as simle as: ```ts import { synth } from '@kbn/esql-ast'; const node = synth.expr `my.field = max(10, ?my_param)`; // { type: 'function', name: '=', args: [ ... ]} ``` To construct an equivalent AST node using the `Builder` class, you would need to write the following code: ```ts import { Builder } from '@kbn/esql-ast'; const node = Builder.expression.func.binary('=', [ Builder.expression.column({ args: [Builder.identifier({ name: 'my' }), Builder.identifier({ name: 'field' })], }), Builder.expression.func.call('max', [ Builder.expression.literal.integer(10), Builder.param.named({ value: 'my_param' }), ]), ]); // { type: 'function', name: '=', args: [ ... ]} ``` You can nest template strings to create more complex AST nodes: ```ts const field = synth.expr `my.field`; const value = synth.expr `max(10, 20)`; const assignment = synth.expr`${field} = ${value}`; // { type: 'function', name: '=', args: [ // { type: 'column', args: [ ... ] }, // { type: 'function', name: 'max', args: [ ... ] } // ]} ``` Use the `synth.cmd` method to create command nodes: ```ts const command = synth.cmd `WHERE my.field == 10`; // { type: 'command', name: 'WHERE', args: [ ... ]} ``` AST nodes created by the synthesizer are pretty-printed when you coerce them to a string or call the `toString` method: ```ts const command = synth.cmd ` WHERE my.field == 10 `; // { type: 'command', ... } String(command); // "WHERE my.field == 10" ``` ## Reference ### `synth.expr` The `synth.expr` synthesizes an expression AST nodes. (*Expressions* are basically any thing that can go into a `WHERE` command, like boolean expression, function call, literal, params, etc.) Use it as a function: ```ts const node = synth.expr('my.field = max(10, 20)'); ``` Specify parser options: ```ts const node = synth.expr('my.field = max(10, 20)', { withFormatting: false }); ``` Use it as a template string tag: ```ts const node = synth.expr `my.field = max(10, 20)`; ``` Specify parser options, when using as a template string tag: ```ts const node = synth.expr({ withFormatting: false }) `my.field = max(10, 20)`; ``` Combine nodes using template strings: ```ts const field = synth.expr `my.field`; const node = synth.expr `${field} = max(10, 20)`; ``` Print the node as a string: ```ts const node = synth.expr `my.field = max(10, 20)`; String(node); // 'my.field = max(10, 20)' ``` ### `synth.cmd` The `synth.cmd` synthesizes a command AST node (such as `SELECT`, `WHERE`, etc.). You use it the same as the `synth.expr` function or template string tag. The only difference is that the `synth.cmd` function or tag creates a command AST node. ```ts const node = synth.cmd `WHERE my.field == 10`; // { type: 'command', name: 'where', args: [ ... ]} ``` ### 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) (cherry picked from commit 378002f)
💚 All backports created successfully
Note: Successful backport PRs will be merged automatically after passing CI. Questions ?Please refer to the Backport tool documentation |
# Backport This will backport the following commits from `main` to `8.x`: - [[ES|QL] AST node synthesizer (#201814)](#201814) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Vadim Kibana","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-02T09:12:36Z","message":"[ES|QL] AST node synthesizer (#201814)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/191812\r\n\r\nAdds ability to create ES|QL AST nodes from plain strings and compose\r\nthem.\r\n\r\nCreate an integer literal:\r\n\r\n```js\r\nconst node = expr `42`;\r\n```\r\n\r\nCreate any expression:\r\n\r\n```js\r\nconst node = expr `nested.field = fn(123)`;\r\n```\r\n\r\nCompose AST nodes:\r\n\r\n```js\r\nconst value = expr `123`;\r\nconst node = expr `nested.field = fn(${value})`;\r\n```\r\n\r\n## Usage\r\n\r\nYou can create an assignment expression AST node as simle as:\r\n\r\n```ts\r\nimport { synth } from '@kbn/esql-ast';\r\n\r\nconst node = synth.expr `my.field = max(10, ?my_param)`;\r\n// { type: 'function', name: '=', args: [ ... ]}\r\n```\r\n\r\nTo construct an equivalent AST node using the `Builder` class, you would\r\nneed to\r\nwrite the following code:\r\n\r\n```ts\r\nimport { Builder } from '@kbn/esql-ast';\r\n\r\nconst node = Builder.expression.func.binary('=', [\r\n Builder.expression.column({\r\n args: [Builder.identifier({ name: 'my' }), Builder.identifier({ name: 'field' })],\r\n }),\r\n Builder.expression.func.call('max', [\r\n Builder.expression.literal.integer(10),\r\n Builder.param.named({ value: 'my_param' }),\r\n ]),\r\n]);\r\n// { type: 'function', name: '=', args: [ ... ]}\r\n```\r\n\r\nYou can nest template strings to create more complex AST nodes:\r\n\r\n```ts\r\nconst field = synth.expr `my.field`;\r\nconst value = synth.expr `max(10, 20)`;\r\n\r\nconst assignment = synth.expr`${field} = ${value}`;\r\n// { type: 'function', name: '=', args: [ \r\n// { type: 'column', args: [ ... ] },\r\n// { type: 'function', name: 'max', args: [ ... ] }\r\n// ]}\r\n```\r\n\r\nUse the `synth.cmd` method to create command nodes:\r\n\r\n```ts\r\nconst command = synth.cmd `WHERE my.field == 10`;\r\n// { type: 'command', name: 'WHERE', args: [ ... ]}\r\n```\r\n\r\nAST nodes created by the synthesizer are pretty-printed when you coerce\r\nthem to\r\na string or call the `toString` method:\r\n\r\n```ts\r\nconst command = synth.cmd ` WHERE my.field == 10 `; // { type: 'command', ... }\r\nString(command); // \"WHERE my.field == 10\"\r\n```\r\n\r\n\r\n## Reference\r\n\r\n### `synth.expr`\r\n\r\nThe `synth.expr` synthesizes an expression AST nodes. (*Expressions* are\r\nbasically any thing that can go into a `WHERE` command, like boolean\r\nexpression,\r\nfunction call, literal, params, etc.)\r\n\r\nUse it as a function:\r\n\r\n```ts\r\nconst node = synth.expr('my.field = max(10, 20)');\r\n```\r\n\r\nSpecify parser options:\r\n\r\n```ts\r\nconst node = synth.expr('my.field = max(10, 20)', { withFormatting: false });\r\n```\r\n\r\nUse it as a template string tag:\r\n\r\n```ts\r\nconst node = synth.expr `my.field = max(10, 20)`;\r\n```\r\n\r\nSpecify parser options, when using as a template string tag:\r\n\r\n```ts\r\nconst node = synth.expr({ withFormatting: false }) `my.field = max(10, 20)`;\r\n```\r\n\r\nCombine nodes using template strings:\r\n\r\n```ts\r\nconst field = synth.expr `my.field`;\r\nconst node = synth.expr `${field} = max(10, 20)`;\r\n```\r\n\r\nPrint the node as a string:\r\n\r\n```ts\r\nconst node = synth.expr `my.field = max(10, 20)`;\r\nString(node); // 'my.field = max(10, 20)'\r\n```\r\n\r\n\r\n### `synth.cmd`\r\n\r\nThe `synth.cmd` synthesizes a command AST node (such as `SELECT`,\r\n`WHERE`,\r\netc.). You use it the same as the `synth.expr` function or template\r\nstring tag.\r\nThe only difference is that the `synth.cmd` function or tag creates a\r\ncommand\r\nAST node.\r\n\r\n```ts\r\nconst node = synth.cmd `WHERE my.field == 10`;\r\n// { type: 'command', name: 'where', args: [ ... ]}\r\n```\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n### For maintainers\r\n\r\n- [x] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)","sha":"378002f9f60356d1d5fe04a6fdf0a7641175ca14","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["review","release_note:skip","v9.0.0","Feature:ES|QL","Team:ESQL","backport:version","v8.18.0"],"title":"[ES|QL] AST node synthesizer","number":201814,"url":"https://github.com/elastic/kibana/pull/201814","mergeCommit":{"message":"[ES|QL] AST node synthesizer (#201814)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/191812\r\n\r\nAdds ability to create ES|QL AST nodes from plain strings and compose\r\nthem.\r\n\r\nCreate an integer literal:\r\n\r\n```js\r\nconst node = expr `42`;\r\n```\r\n\r\nCreate any expression:\r\n\r\n```js\r\nconst node = expr `nested.field = fn(123)`;\r\n```\r\n\r\nCompose AST nodes:\r\n\r\n```js\r\nconst value = expr `123`;\r\nconst node = expr `nested.field = fn(${value})`;\r\n```\r\n\r\n## Usage\r\n\r\nYou can create an assignment expression AST node as simle as:\r\n\r\n```ts\r\nimport { synth } from '@kbn/esql-ast';\r\n\r\nconst node = synth.expr `my.field = max(10, ?my_param)`;\r\n// { type: 'function', name: '=', args: [ ... ]}\r\n```\r\n\r\nTo construct an equivalent AST node using the `Builder` class, you would\r\nneed to\r\nwrite the following code:\r\n\r\n```ts\r\nimport { Builder } from '@kbn/esql-ast';\r\n\r\nconst node = Builder.expression.func.binary('=', [\r\n Builder.expression.column({\r\n args: [Builder.identifier({ name: 'my' }), Builder.identifier({ name: 'field' })],\r\n }),\r\n Builder.expression.func.call('max', [\r\n Builder.expression.literal.integer(10),\r\n Builder.param.named({ value: 'my_param' }),\r\n ]),\r\n]);\r\n// { type: 'function', name: '=', args: [ ... ]}\r\n```\r\n\r\nYou can nest template strings to create more complex AST nodes:\r\n\r\n```ts\r\nconst field = synth.expr `my.field`;\r\nconst value = synth.expr `max(10, 20)`;\r\n\r\nconst assignment = synth.expr`${field} = ${value}`;\r\n// { type: 'function', name: '=', args: [ \r\n// { type: 'column', args: [ ... ] },\r\n// { type: 'function', name: 'max', args: [ ... ] }\r\n// ]}\r\n```\r\n\r\nUse the `synth.cmd` method to create command nodes:\r\n\r\n```ts\r\nconst command = synth.cmd `WHERE my.field == 10`;\r\n// { type: 'command', name: 'WHERE', args: [ ... ]}\r\n```\r\n\r\nAST nodes created by the synthesizer are pretty-printed when you coerce\r\nthem to\r\na string or call the `toString` method:\r\n\r\n```ts\r\nconst command = synth.cmd ` WHERE my.field == 10 `; // { type: 'command', ... }\r\nString(command); // \"WHERE my.field == 10\"\r\n```\r\n\r\n\r\n## Reference\r\n\r\n### `synth.expr`\r\n\r\nThe `synth.expr` synthesizes an expression AST nodes. (*Expressions* are\r\nbasically any thing that can go into a `WHERE` command, like boolean\r\nexpression,\r\nfunction call, literal, params, etc.)\r\n\r\nUse it as a function:\r\n\r\n```ts\r\nconst node = synth.expr('my.field = max(10, 20)');\r\n```\r\n\r\nSpecify parser options:\r\n\r\n```ts\r\nconst node = synth.expr('my.field = max(10, 20)', { withFormatting: false });\r\n```\r\n\r\nUse it as a template string tag:\r\n\r\n```ts\r\nconst node = synth.expr `my.field = max(10, 20)`;\r\n```\r\n\r\nSpecify parser options, when using as a template string tag:\r\n\r\n```ts\r\nconst node = synth.expr({ withFormatting: false }) `my.field = max(10, 20)`;\r\n```\r\n\r\nCombine nodes using template strings:\r\n\r\n```ts\r\nconst field = synth.expr `my.field`;\r\nconst node = synth.expr `${field} = max(10, 20)`;\r\n```\r\n\r\nPrint the node as a string:\r\n\r\n```ts\r\nconst node = synth.expr `my.field = max(10, 20)`;\r\nString(node); // 'my.field = max(10, 20)'\r\n```\r\n\r\n\r\n### `synth.cmd`\r\n\r\nThe `synth.cmd` synthesizes a command AST node (such as `SELECT`,\r\n`WHERE`,\r\netc.). You use it the same as the `synth.expr` function or template\r\nstring tag.\r\nThe only difference is that the `synth.cmd` function or tag creates a\r\ncommand\r\nAST node.\r\n\r\n```ts\r\nconst node = synth.cmd `WHERE my.field == 10`;\r\n// { type: 'command', name: 'where', args: [ ... ]}\r\n```\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n### For maintainers\r\n\r\n- [x] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)","sha":"378002f9f60356d1d5fe04a6fdf0a7641175ca14"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/201814","number":201814,"mergeCommit":{"message":"[ES|QL] AST node synthesizer (#201814)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/191812\r\n\r\nAdds ability to create ES|QL AST nodes from plain strings and compose\r\nthem.\r\n\r\nCreate an integer literal:\r\n\r\n```js\r\nconst node = expr `42`;\r\n```\r\n\r\nCreate any expression:\r\n\r\n```js\r\nconst node = expr `nested.field = fn(123)`;\r\n```\r\n\r\nCompose AST nodes:\r\n\r\n```js\r\nconst value = expr `123`;\r\nconst node = expr `nested.field = fn(${value})`;\r\n```\r\n\r\n## Usage\r\n\r\nYou can create an assignment expression AST node as simle as:\r\n\r\n```ts\r\nimport { synth } from '@kbn/esql-ast';\r\n\r\nconst node = synth.expr `my.field = max(10, ?my_param)`;\r\n// { type: 'function', name: '=', args: [ ... ]}\r\n```\r\n\r\nTo construct an equivalent AST node using the `Builder` class, you would\r\nneed to\r\nwrite the following code:\r\n\r\n```ts\r\nimport { Builder } from '@kbn/esql-ast';\r\n\r\nconst node = Builder.expression.func.binary('=', [\r\n Builder.expression.column({\r\n args: [Builder.identifier({ name: 'my' }), Builder.identifier({ name: 'field' })],\r\n }),\r\n Builder.expression.func.call('max', [\r\n Builder.expression.literal.integer(10),\r\n Builder.param.named({ value: 'my_param' }),\r\n ]),\r\n]);\r\n// { type: 'function', name: '=', args: [ ... ]}\r\n```\r\n\r\nYou can nest template strings to create more complex AST nodes:\r\n\r\n```ts\r\nconst field = synth.expr `my.field`;\r\nconst value = synth.expr `max(10, 20)`;\r\n\r\nconst assignment = synth.expr`${field} = ${value}`;\r\n// { type: 'function', name: '=', args: [ \r\n// { type: 'column', args: [ ... ] },\r\n// { type: 'function', name: 'max', args: [ ... ] }\r\n// ]}\r\n```\r\n\r\nUse the `synth.cmd` method to create command nodes:\r\n\r\n```ts\r\nconst command = synth.cmd `WHERE my.field == 10`;\r\n// { type: 'command', name: 'WHERE', args: [ ... ]}\r\n```\r\n\r\nAST nodes created by the synthesizer are pretty-printed when you coerce\r\nthem to\r\na string or call the `toString` method:\r\n\r\n```ts\r\nconst command = synth.cmd ` WHERE my.field == 10 `; // { type: 'command', ... }\r\nString(command); // \"WHERE my.field == 10\"\r\n```\r\n\r\n\r\n## Reference\r\n\r\n### `synth.expr`\r\n\r\nThe `synth.expr` synthesizes an expression AST nodes. (*Expressions* are\r\nbasically any thing that can go into a `WHERE` command, like boolean\r\nexpression,\r\nfunction call, literal, params, etc.)\r\n\r\nUse it as a function:\r\n\r\n```ts\r\nconst node = synth.expr('my.field = max(10, 20)');\r\n```\r\n\r\nSpecify parser options:\r\n\r\n```ts\r\nconst node = synth.expr('my.field = max(10, 20)', { withFormatting: false });\r\n```\r\n\r\nUse it as a template string tag:\r\n\r\n```ts\r\nconst node = synth.expr `my.field = max(10, 20)`;\r\n```\r\n\r\nSpecify parser options, when using as a template string tag:\r\n\r\n```ts\r\nconst node = synth.expr({ withFormatting: false }) `my.field = max(10, 20)`;\r\n```\r\n\r\nCombine nodes using template strings:\r\n\r\n```ts\r\nconst field = synth.expr `my.field`;\r\nconst node = synth.expr `${field} = max(10, 20)`;\r\n```\r\n\r\nPrint the node as a string:\r\n\r\n```ts\r\nconst node = synth.expr `my.field = max(10, 20)`;\r\nString(node); // 'my.field = max(10, 20)'\r\n```\r\n\r\n\r\n### `synth.cmd`\r\n\r\nThe `synth.cmd` synthesizes a command AST node (such as `SELECT`,\r\n`WHERE`,\r\netc.). You use it the same as the `synth.expr` function or template\r\nstring tag.\r\nThe only difference is that the `synth.cmd` function or tag creates a\r\ncommand\r\nAST node.\r\n\r\n```ts\r\nconst node = synth.cmd `WHERE my.field == 10`;\r\n// { type: 'command', name: 'where', args: [ ... ]}\r\n```\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n### For maintainers\r\n\r\n- [x] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)","sha":"378002f9f60356d1d5fe04a6fdf0a7641175ca14"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Vadim Kibana <[email protected]>
## Summary Closes elastic#191812 Adds ability to create ES|QL AST nodes from plain strings and compose them. Create an integer literal: ```js const node = expr `42`; ``` Create any expression: ```js const node = expr `nested.field = fn(123)`; ``` Compose AST nodes: ```js const value = expr `123`; const node = expr `nested.field = fn(${value})`; ``` ## Usage You can create an assignment expression AST node as simle as: ```ts import { synth } from '@kbn/esql-ast'; const node = synth.expr `my.field = max(10, ?my_param)`; // { type: 'function', name: '=', args: [ ... ]} ``` To construct an equivalent AST node using the `Builder` class, you would need to write the following code: ```ts import { Builder } from '@kbn/esql-ast'; const node = Builder.expression.func.binary('=', [ Builder.expression.column({ args: [Builder.identifier({ name: 'my' }), Builder.identifier({ name: 'field' })], }), Builder.expression.func.call('max', [ Builder.expression.literal.integer(10), Builder.param.named({ value: 'my_param' }), ]), ]); // { type: 'function', name: '=', args: [ ... ]} ``` You can nest template strings to create more complex AST nodes: ```ts const field = synth.expr `my.field`; const value = synth.expr `max(10, 20)`; const assignment = synth.expr`${field} = ${value}`; // { type: 'function', name: '=', args: [ // { type: 'column', args: [ ... ] }, // { type: 'function', name: 'max', args: [ ... ] } // ]} ``` Use the `synth.cmd` method to create command nodes: ```ts const command = synth.cmd `WHERE my.field == 10`; // { type: 'command', name: 'WHERE', args: [ ... ]} ``` AST nodes created by the synthesizer are pretty-printed when you coerce them to a string or call the `toString` method: ```ts const command = synth.cmd ` WHERE my.field == 10 `; // { type: 'command', ... } String(command); // "WHERE my.field == 10" ``` ## Reference ### `synth.expr` The `synth.expr` synthesizes an expression AST nodes. (*Expressions* are basically any thing that can go into a `WHERE` command, like boolean expression, function call, literal, params, etc.) Use it as a function: ```ts const node = synth.expr('my.field = max(10, 20)'); ``` Specify parser options: ```ts const node = synth.expr('my.field = max(10, 20)', { withFormatting: false }); ``` Use it as a template string tag: ```ts const node = synth.expr `my.field = max(10, 20)`; ``` Specify parser options, when using as a template string tag: ```ts const node = synth.expr({ withFormatting: false }) `my.field = max(10, 20)`; ``` Combine nodes using template strings: ```ts const field = synth.expr `my.field`; const node = synth.expr `${field} = max(10, 20)`; ``` Print the node as a string: ```ts const node = synth.expr `my.field = max(10, 20)`; String(node); // 'my.field = max(10, 20)' ``` ### `synth.cmd` The `synth.cmd` synthesizes a command AST node (such as `SELECT`, `WHERE`, etc.). You use it the same as the `synth.expr` function or template string tag. The only difference is that the `synth.cmd` function or tag creates a command AST node. ```ts const node = synth.cmd `WHERE my.field == 10`; // { type: 'command', name: 'where', args: [ ... ]} ``` ### 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)
## Summary Closes elastic#191812 Adds ability to create ES|QL AST nodes from plain strings and compose them. Create an integer literal: ```js const node = expr `42`; ``` Create any expression: ```js const node = expr `nested.field = fn(123)`; ``` Compose AST nodes: ```js const value = expr `123`; const node = expr `nested.field = fn(${value})`; ``` ## Usage You can create an assignment expression AST node as simle as: ```ts import { synth } from '@kbn/esql-ast'; const node = synth.expr `my.field = max(10, ?my_param)`; // { type: 'function', name: '=', args: [ ... ]} ``` To construct an equivalent AST node using the `Builder` class, you would need to write the following code: ```ts import { Builder } from '@kbn/esql-ast'; const node = Builder.expression.func.binary('=', [ Builder.expression.column({ args: [Builder.identifier({ name: 'my' }), Builder.identifier({ name: 'field' })], }), Builder.expression.func.call('max', [ Builder.expression.literal.integer(10), Builder.param.named({ value: 'my_param' }), ]), ]); // { type: 'function', name: '=', args: [ ... ]} ``` You can nest template strings to create more complex AST nodes: ```ts const field = synth.expr `my.field`; const value = synth.expr `max(10, 20)`; const assignment = synth.expr`${field} = ${value}`; // { type: 'function', name: '=', args: [ // { type: 'column', args: [ ... ] }, // { type: 'function', name: 'max', args: [ ... ] } // ]} ``` Use the `synth.cmd` method to create command nodes: ```ts const command = synth.cmd `WHERE my.field == 10`; // { type: 'command', name: 'WHERE', args: [ ... ]} ``` AST nodes created by the synthesizer are pretty-printed when you coerce them to a string or call the `toString` method: ```ts const command = synth.cmd ` WHERE my.field == 10 `; // { type: 'command', ... } String(command); // "WHERE my.field == 10" ``` ## Reference ### `synth.expr` The `synth.expr` synthesizes an expression AST nodes. (*Expressions* are basically any thing that can go into a `WHERE` command, like boolean expression, function call, literal, params, etc.) Use it as a function: ```ts const node = synth.expr('my.field = max(10, 20)'); ``` Specify parser options: ```ts const node = synth.expr('my.field = max(10, 20)', { withFormatting: false }); ``` Use it as a template string tag: ```ts const node = synth.expr `my.field = max(10, 20)`; ``` Specify parser options, when using as a template string tag: ```ts const node = synth.expr({ withFormatting: false }) `my.field = max(10, 20)`; ``` Combine nodes using template strings: ```ts const field = synth.expr `my.field`; const node = synth.expr `${field} = max(10, 20)`; ``` Print the node as a string: ```ts const node = synth.expr `my.field = max(10, 20)`; String(node); // 'my.field = max(10, 20)' ``` ### `synth.cmd` The `synth.cmd` synthesizes a command AST node (such as `SELECT`, `WHERE`, etc.). You use it the same as the `synth.expr` function or template string tag. The only difference is that the `synth.cmd` function or tag creates a command AST node. ```ts const node = synth.cmd `WHERE my.field == 10`; // { type: 'command', name: 'where', args: [ ... ]} ``` ### 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)
Summary
Closes #191812
Adds ability to create ES|QL AST nodes from plain strings and compose them.
Create an integer literal:
Create any expression:
Compose AST nodes:
Usage
You can create an assignment expression AST node as simle as:
To construct an equivalent AST node using the
Builder
class, you would need towrite the following code:
You can nest template strings to create more complex AST nodes:
Use the
synth.cmd
method to create command nodes:AST nodes created by the synthesizer are pretty-printed when you coerce them to
a string or call the
toString
method:Reference
synth.expr
The
synth.expr
synthesizes an expression AST nodes. (Expressions arebasically any thing that can go into a
WHERE
command, like boolean expression,function call, literal, params, etc.)
Use it as a function:
Specify parser options:
Use it as a template string tag:
Specify parser options, when using as a template string tag:
Combine nodes using template strings:
Print the node as a string:
synth.cmd
The
synth.cmd
synthesizes a command AST node (such asSELECT
,WHERE
,etc.). You use it the same as the
synth.expr
function or template string tag.The only difference is that the
synth.cmd
function or tag creates a commandAST node.
Checklist
Delete any items that are not applicable to this PR.
For maintainers