Skip to content

Commit

Permalink
[ES|QL] AST query and mutation APIs for metadata fields (#195364)
Browse files Browse the repository at this point in the history
## Summary

Partially addresses #191812

This PR implements some generic ES|QL AST mutation APIs and specifically
APIs for working with `FROM` command `METADATA` fields:

- `from.metadata.list()` — List all `METADATA` fields.
- `from.metadata.find()` — Find a `METADATA` field by name.
- `from.metadata.removeByPredicate()` — Remove a `METADATA` field
by matching a predicate.
- `from.metadata.remove()` — Remove a `METADATA` field by name.
- `from.metadata.insert()` — Insert a `METADATA` field.
- `from.metadata.upsert()` — Insert `METADATA` field, if it does
not exist.


### 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#kibana-release-notes-process)
  • Loading branch information
vadimkibana authored Oct 8, 2024
1 parent cfc8aaf commit a819d65
Show file tree
Hide file tree
Showing 15 changed files with 1,022 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/kbn-esql-ast/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Contents of this package:
- [`walker` — Contains the ES|QL AST `Walker` utility](./src/walker/README.md).
- [`visitor` — Contains the ES|QL AST `Visitor` utility](./src/visitor/README.md).
- [`pretty_print` — Contains code for formatting AST to text](./src/pretty_print/README.md).
- [`mutate` — Contains code for traversing and mutating the AST.](./src/mutate/README.md).


## Demo
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-esql-ast/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ export {
} from './src/pretty_print';

export { EsqlQuery } from './src/query';

export * as mutate from './src/mutate';
32 changes: 30 additions & 2 deletions packages/kbn-esql-ast/src/builder/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
import {
ESQLAstComment,
ESQLAstQueryExpression,
ESQLColumn,
ESQLCommand,
ESQLCommandOption,
ESQLDecimalLiteral,
ESQLInlineCast,
ESQLIntegerLiteral,
ESQLList,
ESQLLocation,
ESQLSource,
} from '../types';
import { AstNodeParserFields, AstNodeTemplate } from './types';
import { AstNodeParserFields, AstNodeTemplate, PartialFields } from './types';

export namespace Builder {
/**
Expand All @@ -38,16 +40,29 @@ export namespace Builder {
});

export const command = (
template: AstNodeTemplate<ESQLCommand>,
template: PartialFields<AstNodeTemplate<ESQLCommand>, 'args'>,
fromParser?: Partial<AstNodeParserFields>
): ESQLCommand => {
return {
...template,
...Builder.parserFields(fromParser),
args: template.args ?? [],
type: 'command',
};
};

export const option = (
template: PartialFields<AstNodeTemplate<ESQLCommandOption>, 'args'>,
fromParser?: Partial<AstNodeParserFields>
): ESQLCommandOption => {
return {
...template,
...Builder.parserFields(fromParser),
args: template.args ?? [],
type: 'option',
};
};

export const comment = (
subtype: ESQLAstComment['subtype'],
text: string,
Expand Down Expand Up @@ -85,6 +100,19 @@ export namespace Builder {
};
};

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

export const inlineCast = (
template: Omit<AstNodeTemplate<ESQLInlineCast>, 'name'>,
fromParser?: Partial<AstNodeParserFields>
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-esql-ast/src/builder/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ export type AstNodeTemplate<Node extends ESQLProperNode> = Omit<
'type' | 'text' | 'location' | 'incomplete'
> &
Partial<Omit<Node, 'type'>>;

export type PartialFields<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
36 changes: 36 additions & 0 deletions packages/kbn-esql-ast/src/mutate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Mutation API

The ES|QL mutation API provides methods to navigate and modify the AST.


## Usage

For example, insert a `FROM` command `METADATA` field:

```typescript
import { parse, mutate, BasicPrettyPrinter } from '@elastic/esql';

const { root } = parse('FROM index METADATA _lang');

console.log([...mutate.commands.from.metadata.list(root)]); // [ '_lang' ]

mutate.commands.from.metadata.upsert(root, '_id');

console.log([...mutate.commands.from.metadata.list(root)]); // [ '_lang', '_id' ]

const src = BasicPrettyPrinter.print(root);

console.log(src); // FROM index METADATA _lang, _id
```


## API

- `.commands.from.metadata.list()` &mdash; List all `METADATA` fields.
- `.commands.from.metadata.find()` &mdash; Find a `METADATA` field by name.
- `.commands.from.metadata.removeByPredicate()` &mdash; Remove a `METADATA`
field by matching a predicate.
- `.commands.from.metadata.remove()` &mdash; Remove a `METADATA` field by name.
- `.commands.from.metadata.insert()` &mdash; Insert a `METADATA` field.
- `.commands.from.metadata.upsert()` &mdash; Insert `METADATA` field, if it does
not exist.
12 changes: 12 additions & 0 deletions packages/kbn-esql-ast/src/mutate/commands/from/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import * as metadata from './metadata';

export { metadata };
Loading

0 comments on commit a819d65

Please sign in to comment.