-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ES|QL]
JOIN
command autocomplete and validation (#205762)
## Summary Part of #200858 Main goal of this PR is to introduce initial autocomplete for the `JOIN` command: ![Kapture 2025-01-09 at 19 02 17](https://github.com/user-attachments/assets/5ecaddb7-d8c1-4768-a22d-82d2adc521ce) In this PR: - Adds `JOIN` command and `AS` function definition - Adds `JOIN` command validation - Adds `JOIN` command autocomplete - New command suggestion, including command type - Command suggestion on partial command entry - Suggests lookup indices - Fetches them from the server and caches - Also suggests lookup index aliases - Suggests `ON` keyword - Suggests join condition fields - Suggests `,` or `|` after join condition fields - Autocomplete behaviour that could be improve in followup - After index suggestion selection, the "ON" suggestion does not appear automatically, user needs to enter space ` `. - When suggesting `ON <condition>` fields, compute lookup index and source index field intersection and show only those. - Only `LOOKUP JOIN` is exposed. `LEFT JOIN` and `RIGTH JOIN` are defined in code, but commented out. - The aliasing using `AS` operator will validate, but autocomplete does not actively suggest it to the user. --- ### Testing To test, you can create lookup indices in dev console using the following queries: ``` PUT /lookup_index { "settings": { "index.mode": "lookup" }, "mappings": { "properties": { "currency": { "type": "keyword" } } } } PUT /lookup_index_with_alias { "settings": { "index.mode": "lookup" }, "aliases": { "lookup_index2_alias1": {}, "lookup_index2_alias2": {} } } ``` Add some sample data: ``` POST /lookup_index/_doc { "currency": "EUR", "continenet": "Europe", "name": "Euro" } POST /lookup_index/_doc { "currency": "USD", "continenet": "North America", "name": "US Dollar" } POST /lookup_index/_doc { "currency": "USD", "continenet": "North America", "name": "Canadian Dollar" } ``` Add `kibana_sample_data_ecommerce` sample data and execute a query: ``` FROM kibana_sample_data_ecommerce | LOOKUP JOIN lookup_index ON currency ``` --- ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [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 --------- Co-authored-by: kibanamachine <[email protected]>
- Loading branch information
1 parent
e7f0771
commit 571ee96
Showing
42 changed files
with
1,256 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
...esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.join.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* 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 { setup, getFieldNamesByType } from './helpers'; | ||
|
||
describe('autocomplete.suggest', () => { | ||
describe('<type> JOIN <index> [ AS <alias> ] ON <condition> [, <condition> [, ...]]', () => { | ||
describe('<type> JOIN ...', () => { | ||
test('suggests join commands', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | /'); | ||
const filtered = suggestions | ||
.filter((s) => s.label.includes('JOIN')) | ||
.map((s) => [s.label, s.text, s.detail]); | ||
|
||
expect(filtered.map((s) => s[0])).toEqual(['LOOKUP JOIN']); | ||
|
||
// TODO: Uncomment when other join types are implemented | ||
// expect(filtered.map((s) => s[0])).toEqual(['LEFT JOIN', 'RIGHT JOIN', 'LOOKUP JOIN']); | ||
}); | ||
|
||
test('can infer full command name based on the unique command type', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKU/'); | ||
const filtered = suggestions.filter((s) => s.label.toUpperCase() === 'LOOKUP JOIN'); | ||
|
||
expect(filtered[0].label).toBe('LOOKUP JOIN'); | ||
}); | ||
|
||
test('suggests command on first character', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP J/'); | ||
const filtered = suggestions.filter((s) => s.label.toUpperCase() === 'LOOKUP JOIN'); | ||
|
||
expect(filtered[0].label).toBe('LOOKUP JOIN'); | ||
}); | ||
|
||
test('returns command description, correct type, and suggestion continuation', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP J/'); | ||
|
||
expect(suggestions[0]).toMatchObject({ | ||
label: 'LOOKUP JOIN', | ||
text: 'LOOKUP JOIN $0', | ||
detail: 'Join with a "lookup" mode index', | ||
kind: 'Keyword', | ||
}); | ||
}); | ||
}); | ||
|
||
describe('... <index> ...', () => { | ||
test('can suggest lookup indices (and aliases)', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LEFT JOIN /'); | ||
const labels = suggestions.map((s) => s.label); | ||
|
||
expect(labels).toEqual([ | ||
'join_index', | ||
'join_index_with_alias', | ||
'join_index_alias_1', | ||
'join_index_alias_2', | ||
]); | ||
}); | ||
|
||
test('discriminates between indices and aliases', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LEFT JOIN /'); | ||
const indices: string[] = suggestions | ||
.filter((s) => s.detail === 'Index') | ||
.map((s) => s.label) | ||
.sort(); | ||
const aliases: string[] = suggestions | ||
.filter((s) => s.detail === 'Alias') | ||
.map((s) => s.label) | ||
.sort(); | ||
|
||
expect(indices).toEqual(['join_index', 'join_index_with_alias']); | ||
expect(aliases).toEqual(['join_index_alias_1', 'join_index_alias_2']); | ||
}); | ||
}); | ||
|
||
describe('... ON <condition>', () => { | ||
test('shows "ON" keyword suggestion', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP JOIN join_index /'); | ||
const labels = suggestions.map((s) => s.label); | ||
|
||
expect(labels).toEqual(['ON']); | ||
}); | ||
|
||
test('suggests fields after ON keyword', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP JOIN join_index ON /'); | ||
const labels = suggestions.map((s) => s.text).sort(); | ||
const expected = getFieldNamesByType('any') | ||
.sort() | ||
.map((field) => field + ' '); | ||
|
||
expect(labels).toEqual(expected); | ||
}); | ||
|
||
test('more field suggestions after comma', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP JOIN join_index ON stringField, /'); | ||
const labels = suggestions.map((s) => s.text).sort(); | ||
const expected = getFieldNamesByType('any') | ||
.sort() | ||
.map((field) => field + ' '); | ||
|
||
expect(labels).toEqual(expected); | ||
}); | ||
|
||
test('suggests pipe and comma after a field', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP JOIN join_index ON stringField /'); | ||
const labels = suggestions.map((s) => s.label).sort(); | ||
|
||
expect(labels).toEqual([',', '|']); | ||
}); | ||
|
||
test('suggests pipe and comma after a field (no space)', async () => { | ||
const { suggest } = await setup(); | ||
|
||
const suggestions = await suggest('FROM index | LOOKUP JOIN join_index ON stringField/'); | ||
const labels = suggestions.map((s) => s.label).sort(); | ||
|
||
expect(labels).toEqual([',', '|']); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.