Skip to content

Commit d77dc36

Browse files
stratoulaalbertoblaz
authored andcommitted
[ES|QL] Supports multi values variables in MV_CONTAINS (elastic#239266)
## Summary Closes elastic#237228 Supports multivalue support when the user uses the variable in mv_contains both in Discover and Dashboards. ![meow](https://github.com/user-attachments/assets/07778277-e2e0-4a53-bd3a-9097f8adf2b8) As the issue indicates: - Included a multi-select option only when creating a control for multivalues either static values or values from a query - This is not possible for fields (??field) - The multi-select option is OFF by default unless the user creates the variable control coming from the `MV_CONTAINS` function - Supported for both Discover and Dashboard - Based on the existing controls functionality for multi-selections <img width="550" height="228" alt="image" src="https://github.com/user-attachments/assets/58c6c17b-56a7-41c4-b6b6-d1fc00c314dd" /> <img width="542" height="683" alt="image" src="https://github.com/user-attachments/assets/c18cea19-f7b1-46e2-a001-79de3d9396c5" /> ### Checklist - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed
1 parent 57ba326 commit d77dc36

File tree

27 files changed

+522
-79
lines changed

27 files changed

+522
-79
lines changed

src/platform/packages/private/kbn-esql-editor/src/esql_editor.tsx

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -336,56 +336,27 @@ const ESQLEditorInternal = function ESQLEditor({
336336
openTimePickerPopover();
337337
});
338338

339-
monaco.editor.registerCommand('esql.control.time_literal.create', async (...args) => {
340-
const position = editor1.current?.getPosition();
341-
await triggerControl(
342-
fixedQuery,
343-
ESQLVariableType.TIME_LITERAL,
344-
position,
345-
uiActions,
346-
esqlVariables,
347-
controlsContext?.onSaveControl,
348-
controlsContext?.onCancelControl
349-
);
350-
});
351-
352-
monaco.editor.registerCommand('esql.control.fields.create', async (...args) => {
353-
const position = editor1.current?.getPosition();
354-
await triggerControl(
355-
fixedQuery,
356-
ESQLVariableType.FIELDS,
357-
position,
358-
uiActions,
359-
esqlVariables,
360-
controlsContext?.onSaveControl,
361-
controlsContext?.onCancelControl
362-
);
363-
});
364-
365-
monaco.editor.registerCommand('esql.control.values.create', async (...args) => {
366-
const position = editor1.current?.getPosition();
367-
await triggerControl(
368-
fixedQuery,
369-
ESQLVariableType.VALUES,
370-
position,
371-
uiActions,
372-
esqlVariables,
373-
controlsContext?.onSaveControl,
374-
controlsContext?.onCancelControl
375-
);
376-
});
377-
378-
monaco.editor.registerCommand('esql.control.functions.create', async (...args) => {
379-
const position = editor1.current?.getPosition();
380-
await triggerControl(
381-
fixedQuery,
382-
ESQLVariableType.FUNCTIONS,
383-
position,
384-
uiActions,
385-
esqlVariables,
386-
controlsContext?.onSaveControl,
387-
controlsContext?.onCancelControl
388-
);
339+
const controlCommands = [
340+
{ command: 'esql.control.multi_values.create', variableType: ESQLVariableType.MULTI_VALUES },
341+
{ command: 'esql.control.time_literal.create', variableType: ESQLVariableType.TIME_LITERAL },
342+
{ command: 'esql.control.fields.create', variableType: ESQLVariableType.FIELDS },
343+
{ command: 'esql.control.values.create', variableType: ESQLVariableType.VALUES },
344+
{ command: 'esql.control.functions.create', variableType: ESQLVariableType.FUNCTIONS },
345+
];
346+
347+
controlCommands.forEach(({ command, variableType }) => {
348+
monaco.editor.registerCommand(command, async (...args) => {
349+
const position = editor1.current?.getPosition();
350+
await triggerControl(
351+
fixedQuery,
352+
variableType,
353+
position,
354+
uiActions,
355+
esqlVariables,
356+
controlsContext?.onSaveControl,
357+
controlsContext?.onCancelControl
358+
);
359+
});
389360
});
390361

391362
editor1.current?.addCommand(

src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
511511
queryDsl: `${ELASTIC_DOCS}explore-analyze/query-filter/languages/querydsl`,
512512
queryESQL: `${ELASTIC_DOCS}explore-analyze/query-filter/languages/esql`,
513513
queryESQLExamples: `${ELASTIC_DOCS}explore-analyze/query-filter/languages/esql`,
514+
queryESQLMultiValueControls: `${ELASTIC_DOCS}explore-analyze/query-filter/languages/esql-kibana#esql-multi-values-controls`,
514515
},
515516
search: {
516517
sessions: `${ELASTIC_DOCS}explore-analyze/discover/search-sessions`,

src/platform/packages/shared/kbn-doc-links/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ export interface DocLinks {
368368
readonly queryDsl: string;
369369
readonly queryESQL: string;
370370
readonly queryESQLExamples: string;
371+
readonly queryESQLMultiValueControls: string;
371372
};
372373
readonly date: {
373374
readonly dateMath: string;

src/platform/packages/shared/kbn-es-types/src/search.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,5 +686,10 @@ export interface ESQLSearchParams {
686686
dropNullColumns?: boolean;
687687
params?:
688688
| estypes.ScalarValue[]
689-
| Array<Record<string, string | number | Record<string, string | number> | undefined>>;
689+
| Array<
690+
Record<
691+
string,
692+
string | number | (string | number)[] | Record<string, string | number> | undefined
693+
>
694+
>;
690695
}

src/platform/packages/shared/kbn-esql-ast/scripts/functions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ export function enrichFunctionParameters(functionDefinition: FunctionDefinition)
167167
});
168168
}
169169

170+
if (functionDefinition.name === 'mv_contains') {
171+
return enrichFunctionSignatures(functionDefinition, 'superset', {
172+
supportsMultiValues: true,
173+
});
174+
}
175+
170176
if (functionDefinition.name === 'qstr') {
171177
return {
172178
...functionDefinition,

src/platform/packages/shared/kbn-esql-ast/src/definitions/generated/scalar_functions.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7582,6 +7582,7 @@ const mvContainsDefinition: FunctionDefinition = {
75827582
name: 'superset',
75837583
type: 'boolean',
75847584
optional: false,
7585+
supportsMultiValues: true,
75857586
},
75867587
{
75877588
name: 'subset',
@@ -7597,6 +7598,7 @@ const mvContainsDefinition: FunctionDefinition = {
75977598
name: 'superset',
75987599
type: 'cartesian_point',
75997600
optional: false,
7601+
supportsMultiValues: true,
76007602
},
76017603
{
76027604
name: 'subset',
@@ -7612,6 +7614,7 @@ const mvContainsDefinition: FunctionDefinition = {
76127614
name: 'superset',
76137615
type: 'cartesian_shape',
76147616
optional: false,
7617+
supportsMultiValues: true,
76157618
},
76167619
{
76177620
name: 'subset',
@@ -7627,6 +7630,7 @@ const mvContainsDefinition: FunctionDefinition = {
76277630
name: 'superset',
76287631
type: 'date',
76297632
optional: false,
7633+
supportsMultiValues: true,
76307634
},
76317635
{
76327636
name: 'subset',
@@ -7642,6 +7646,7 @@ const mvContainsDefinition: FunctionDefinition = {
76427646
name: 'superset',
76437647
type: 'date_nanos',
76447648
optional: false,
7649+
supportsMultiValues: true,
76457650
},
76467651
{
76477652
name: 'subset',
@@ -7657,6 +7662,7 @@ const mvContainsDefinition: FunctionDefinition = {
76577662
name: 'superset',
76587663
type: 'double',
76597664
optional: false,
7665+
supportsMultiValues: true,
76607666
},
76617667
{
76627668
name: 'subset',
@@ -7672,6 +7678,7 @@ const mvContainsDefinition: FunctionDefinition = {
76727678
name: 'superset',
76737679
type: 'geo_point',
76747680
optional: false,
7681+
supportsMultiValues: true,
76757682
},
76767683
{
76777684
name: 'subset',
@@ -7687,6 +7694,7 @@ const mvContainsDefinition: FunctionDefinition = {
76877694
name: 'superset',
76887695
type: 'geo_shape',
76897696
optional: false,
7697+
supportsMultiValues: true,
76907698
},
76917699
{
76927700
name: 'subset',
@@ -7702,6 +7710,7 @@ const mvContainsDefinition: FunctionDefinition = {
77027710
name: 'superset',
77037711
type: 'geohash',
77047712
optional: false,
7713+
supportsMultiValues: true,
77057714
},
77067715
{
77077716
name: 'subset',
@@ -7717,6 +7726,7 @@ const mvContainsDefinition: FunctionDefinition = {
77177726
name: 'superset',
77187727
type: 'geohex',
77197728
optional: false,
7729+
supportsMultiValues: true,
77207730
},
77217731
{
77227732
name: 'subset',
@@ -7732,6 +7742,7 @@ const mvContainsDefinition: FunctionDefinition = {
77327742
name: 'superset',
77337743
type: 'geotile',
77347744
optional: false,
7745+
supportsMultiValues: true,
77357746
},
77367747
{
77377748
name: 'subset',
@@ -7747,6 +7758,7 @@ const mvContainsDefinition: FunctionDefinition = {
77477758
name: 'superset',
77487759
type: 'integer',
77497760
optional: false,
7761+
supportsMultiValues: true,
77507762
},
77517763
{
77527764
name: 'subset',
@@ -7762,6 +7774,7 @@ const mvContainsDefinition: FunctionDefinition = {
77627774
name: 'superset',
77637775
type: 'ip',
77647776
optional: false,
7777+
supportsMultiValues: true,
77657778
},
77667779
{
77677780
name: 'subset',
@@ -7777,6 +7790,7 @@ const mvContainsDefinition: FunctionDefinition = {
77777790
name: 'superset',
77787791
type: 'keyword',
77797792
optional: false,
7793+
supportsMultiValues: true,
77807794
},
77817795
{
77827796
name: 'subset',
@@ -7792,6 +7806,7 @@ const mvContainsDefinition: FunctionDefinition = {
77927806
name: 'superset',
77937807
type: 'keyword',
77947808
optional: false,
7809+
supportsMultiValues: true,
77957810
},
77967811
{
77977812
name: 'subset',
@@ -7807,6 +7822,7 @@ const mvContainsDefinition: FunctionDefinition = {
78077822
name: 'superset',
78087823
type: 'long',
78097824
optional: false,
7825+
supportsMultiValues: true,
78107826
},
78117827
{
78127828
name: 'subset',
@@ -7822,6 +7838,7 @@ const mvContainsDefinition: FunctionDefinition = {
78227838
name: 'superset',
78237839
type: 'text',
78247840
optional: false,
7841+
supportsMultiValues: true,
78257842
},
78267843
{
78277844
name: 'subset',
@@ -7837,6 +7854,7 @@ const mvContainsDefinition: FunctionDefinition = {
78377854
name: 'superset',
78387855
type: 'text',
78397856
optional: false,
7857+
supportsMultiValues: true,
78407858
},
78417859
{
78427860
name: 'subset',
@@ -7852,6 +7870,7 @@ const mvContainsDefinition: FunctionDefinition = {
78527870
name: 'superset',
78537871
type: 'unsigned_long',
78547872
optional: false,
7873+
supportsMultiValues: true,
78557874
},
78567875
{
78577876
name: 'subset',
@@ -7867,6 +7886,7 @@ const mvContainsDefinition: FunctionDefinition = {
78677886
name: 'superset',
78687887
type: 'version',
78697888
optional: false,
7889+
supportsMultiValues: true,
78707890
},
78717891
{
78727892
name: 'subset',

src/platform/packages/shared/kbn-esql-ast/src/definitions/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ export interface FunctionParameter {
183183
suggestedValues?: string[];
184184

185185
mapParams?: string;
186+
187+
/** If true, this parameter supports multiple values (arrays). Default is false.
188+
* This indicates that the parameter can accept multiple values, which will be passed as an array.
189+
*/
190+
supportsMultiValues?: boolean;
186191
}
187192

188193
export interface ElasticsearchCommandDefinition {

src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/expressions/positions/empty_expression.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,15 @@ async function buildFieldAndFunctionSuggestions(
206206
const hasNonConstantParam = paramDefinitions.some(({ constantOnly }) => !constantOnly);
207207
const isVariadicOrUnknownPosition = paramDefinitions.length === 0;
208208
if (hasNonConstantParam || isVariadicOrUnknownPosition) {
209+
const canBeMultiValue = paramDefinitions.some(
210+
(t) => t && (t.supportsMultiValues === true || t.name === 'values')
211+
);
209212
await builder.addFields({
210213
types: config.acceptedTypes,
211214
ignoredColumns,
212215
addComma: config.shouldAddComma,
213216
promoteToTop: true,
217+
canBeMultiValue,
214218
});
215219
}
216220

src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/expressions/suggestion_builder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class SuggestionBuilder {
3232
promoteToTop?: boolean;
3333
openSuggestions?: boolean;
3434
values?: boolean;
35+
canBeMultiValue?: boolean;
3536
}): Promise<this> {
3637
const types = options?.types ?? ['any'];
3738
const addComma = options?.addComma ?? false;
@@ -40,6 +41,7 @@ export class SuggestionBuilder {
4041
const ignoredColumns = options?.ignoredColumns ?? [];
4142
const openSuggestions = options?.openSuggestions ?? (addSpaceAfterField || addComma);
4243
const values = options?.values;
44+
const canBeMultiValue = options?.canBeMultiValue ?? false;
4345

4446
const getByType = this.context.callbacks?.getByType ?? (() => Promise.resolve([]));
4547

@@ -50,6 +52,7 @@ export class SuggestionBuilder {
5052
addComma,
5153
promoteToTop,
5254
values,
55+
canBeMultiValue,
5356
});
5457

5558
this.suggestions.push(...fieldSuggestions);

src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/helpers.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ interface FieldSuggestionsOptions {
142142
openSuggestions?: boolean;
143143
addComma?: boolean;
144144
promoteToTop?: boolean;
145+
canBeMultiValue?: boolean;
145146
}
146147

147148
export async function getFieldsSuggestions(
@@ -156,13 +157,20 @@ export async function getFieldsSuggestions(
156157
openSuggestions = false,
157158
addComma = false,
158159
promoteToTop = true,
160+
canBeMultiValue = false,
159161
} = options;
160162

163+
const variableType = (() => {
164+
if (canBeMultiValue) return ESQLVariableType.MULTI_VALUES;
165+
if (values) return ESQLVariableType.VALUES;
166+
return ESQLVariableType.FIELDS;
167+
})();
168+
161169
const suggestions = await getFieldsByType(types, ignoreColumns, {
162170
advanceCursor: addSpaceAfterField,
163171
openSuggestions,
164172
addComma,
165-
variableType: values ? ESQLVariableType.VALUES : ESQLVariableType.FIELDS,
173+
variableType,
166174
});
167175

168176
return pushItUpInTheList(suggestions as ISuggestionItem[], promoteToTop);

0 commit comments

Comments
 (0)