Skip to content

Commit

Permalink
[ES|QL] Update bucket signature (elastic#181787)
Browse files Browse the repository at this point in the history
## Summary

Wraps in the changes from
elastic/elasticsearch#107272

<img width="491" alt="Screenshot 2024-04-25 at 4 46 31 PM"
src="https://github.com/elastic/kibana/assets/315764/4fb3db49-7702-466b-b1fd-b22ca3ed7a0d">



### Checklist

- [ ] [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 (still needs to
be done... waiting on
elastic/elasticsearch#107918)
  • Loading branch information
drewdaemon authored Apr 26, 2024
1 parent 9ae3b64 commit 5c69e1f
Show file tree
Hide file tree
Showing 9 changed files with 400 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { TRIGGER_SUGGESTION_COMMAND } from './factories';
import { camelCase } from 'lodash';
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
import { SuggestionRawDefinition } from './types';
import { groupingFunctionDefinitions } from '../definitions/grouping';

const triggerCharacters = [',', '(', '=', ' '];

Expand Down Expand Up @@ -84,14 +85,22 @@ function getFunctionSignaturesByReturnType(
_expectedReturnType: string | string[],
{
agg,
grouping,
evalMath,
builtin,
// skipAssign here is used to communicate to not propose an assignment if it's not possible
// within the current context (the actual logic has it, but here we want a shortcut)
skipAssign,
}: { agg?: boolean; evalMath?: boolean; builtin?: boolean; skipAssign?: boolean } = {},
}: {
agg?: boolean;
grouping?: boolean;
evalMath?: boolean;
builtin?: boolean;
skipAssign?: boolean;
} = {},
paramsTypes?: string[],
ignored?: string[]
ignored?: string[],
option?: string
) {
const expectedReturnType = Array.isArray(_expectedReturnType)
? _expectedReturnType
Expand All @@ -101,6 +110,9 @@ function getFunctionSignaturesByReturnType(
if (agg) {
list.push(...statsAggregationFunctionDefinitions);
}
if (grouping) {
list.push(...groupingFunctionDefinitions);
}
// eval functions (eval is a special keyword in JS)
if (evalMath) {
list.push(...evalFunctionsDefinitions);
Expand All @@ -109,11 +121,11 @@ function getFunctionSignaturesByReturnType(
list.push(...builtinFunctions.filter(({ name }) => (skipAssign ? name !== '=' : true)));
}
return list
.filter(({ signatures, ignoreAsSuggestion, supportedCommands, name }) => {
.filter(({ signatures, ignoreAsSuggestion, supportedCommands, supportedOptions, name }) => {
if (ignoreAsSuggestion) {
return false;
}
if (!supportedCommands.includes(command)) {
if (!supportedCommands.includes(command) && !supportedOptions?.includes(option || '')) {
return false;
}
const filteredByReturnType = signatures.filter(
Expand Down Expand Up @@ -144,6 +156,7 @@ function getFunctionSignaturesByReturnType(
}
return true;
})
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
.map(({ type, name, signatures }) => {
if (type === 'builtin') {
return signatures.some(({ params }) => params.length > 1) ? `${name} $0` : name;
Expand Down Expand Up @@ -260,7 +273,9 @@ describe('autocomplete', () => {
);
const suggestionInertTextSorted = suggestions
// simulate the editor behaviour for sorting suggestions
.sort((a, b) => (a.sortText || '').localeCompare(b.sortText || ''));
// copied from https://github.com/microsoft/vscode/blob/0a141d23179c76c5771df25a43546d9d9b6ed71c/src/vs/workbench/contrib/testing/browser/testingDecorations.ts#L971-L972
// still not sure how accurate this is...
.sort((a, b) => (a.sortText || a.label).localeCompare(b.sortText || b.label));

expect(suggestionInertTextSorted).toHaveLength(expected.length);
for (const [index, receivedSuggestion] of suggestionInertTextSorted.entries()) {
Expand Down Expand Up @@ -593,15 +608,23 @@ describe('autocomplete', () => {
const allAggFunctions = getFunctionSignaturesByReturnType('stats', 'any', {
agg: true,
});
const allEvaFunctions = getFunctionSignaturesByReturnType('stats', 'any', {
evalMath: true,
});
const allEvaFunctions = getFunctionSignaturesByReturnType(
'stats',
'any',
{
evalMath: true,
grouping: true,
},
undefined,
undefined,
'by'
);
testSuggestions('from a | stats ', ['var0 =', ...allAggFunctions, ...allEvaFunctions]);
testSuggestions('from a | stats a ', [
{ text: '= $0', asSnippet: true, command: TRIGGER_SUGGESTION_COMMAND },
]);
testSuggestions('from a | stats a=', [...allAggFunctions, ...allEvaFunctions]);
testSuggestions('from a | stats a=max(b) by ', [
testSuggestions.only('from a | stats a=max(b) by ', [
'var0 =',
...getFieldNamesByType('any'),
...allEvaFunctions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,15 @@

import { i18n } from '@kbn/i18n';
import type { SuggestionRawDefinition } from './types';
import { statsAggregationFunctionDefinitions } from '../definitions/aggs';
import { builtinFunctions } from '../definitions/builtin';
import { evalFunctionsDefinitions } from '../definitions/functions';
import { getAllCommands } from '../shared/helpers';
import {
getSuggestionFunctionDefinition,
getSuggestionBuiltinDefinition,
getSuggestionCommandDefinition,
TRIGGER_SUGGESTION_COMMAND,
buildConstantsDefinitions,
} from './factories';

export const mathCommandDefinition: SuggestionRawDefinition[] = evalFunctionsDefinitions.map(
getSuggestionFunctionDefinition
);

export const aggregationFunctionsDefinitions: SuggestionRawDefinition[] =
statsAggregationFunctionDefinitions.map(getSuggestionFunctionDefinition);

export function getAssignmentDefinitionCompletitionItem() {
const assignFn = builtinFunctions.find(({ name }) => name === '=')!;
return getSuggestionBuiltinDefinition(assignFn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { i18n } from '@kbn/i18n';
import { SuggestionRawDefinition } from './types';
import { groupingFunctionDefinitions } from '../definitions/grouping';
import { statsAggregationFunctionDefinitions } from '../definitions/aggs';
import { evalFunctionsDefinitions } from '../definitions/functions';
import { getFunctionSignatures, getCommandSignature } from '../definitions/helpers';
Expand All @@ -22,7 +23,9 @@ import { getCommandDefinition, shouldBeQuotedText } from '../shared/helpers';
import { buildDocumentation, buildFunctionDocumentation } from './documentation_util';
import { DOUBLE_BACKTICK, SINGLE_TICK_REGEX } from '../shared/constants';

const allFunctions = statsAggregationFunctionDefinitions.concat(evalFunctionsDefinitions);
const allFunctions = statsAggregationFunctionDefinitions
.concat(evalFunctionsDefinitions)
.concat(groupingFunctionDefinitions);

export const TRIGGER_SUGGESTION_COMMAND = {
title: 'Trigger Suggestion Dialog',
Expand Down Expand Up @@ -75,11 +78,13 @@ export const getCompatibleFunctionDefinition = (
returnTypes?: string[],
ignored: string[] = []
): SuggestionRawDefinition[] => {
const fnSupportedByCommand = allFunctions.filter(
({ name, supportedCommands, supportedOptions }) =>
(option ? supportedOptions?.includes(option) : supportedCommands.includes(command)) &&
!ignored.includes(name)
);
const fnSupportedByCommand = allFunctions
.filter(
({ name, supportedCommands, supportedOptions }) =>
(option ? supportedOptions?.includes(option) : supportedCommands.includes(command)) &&
!ignored.includes(name)
)
.sort((a, b) => a.name.localeCompare(b.name));
if (!returnTypes) {
return fnSupportedByCommand.map(getSuggestionFunctionDefinition);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,72 +573,6 @@ export const evalFunctionsDefinitions: FunctionDefinition[] = [
},
],
},
{
name: 'bucket',
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.autoBucketDoc', {
defaultMessage: `Automatically bucket dates based on a given range and bucket target.`,
}),
signatures: [
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'string', constantOnly: true },
{ name: 'endDate', type: 'string', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'date', constantOnly: true },
{ name: 'endDate', type: 'date', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'string', constantOnly: true },
{ name: 'endDate', type: 'date', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'date', constantOnly: true },
{ name: 'endDate', type: 'string', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'number' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startValue', type: 'number', constantOnly: true },
{ name: 'endValue', type: 'number', constantOnly: true },
],
returnType: 'number',
examples: ['from index | eval bs = bucket(salary, 20, 25324, 74999)'],
},
],
},
{
name: 'case',
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.caseDoc', {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

import { i18n } from '@kbn/i18n';
import { FunctionDefinition } from './types';

export const groupingFunctionDefinitions: FunctionDefinition[] = [
{
name: 'bucket',
alias: ['bin'],
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.autoBucketDoc', {
defaultMessage: `Automatically bucket dates based on a given range and bucket target.`,
}),
type: 'grouping',
supportedCommands: [],
supportedOptions: ['by'],
signatures: [
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'time_literal', constantOnly: true },
],
returnType: 'date',
examples: ['from index | eval hd = bucket(hire_date, 1 hour)'],
},
{
params: [
{ name: 'field', type: 'number' },
{ name: 'buckets', type: 'time_literal', constantOnly: true },
],
returnType: 'number',
examples: ['from index | eval hd = bucket(bytes, 1 hour)'],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'string', constantOnly: true },
{ name: 'endDate', type: 'string', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'date', constantOnly: true },
{ name: 'endDate', type: 'date', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'string', constantOnly: true },
{ name: 'endDate', type: 'date', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'date' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startDate', type: 'date', constantOnly: true },
{ name: 'endDate', type: 'string', constantOnly: true },
],
returnType: 'date',
examples: [
'from index | eval hd = bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")',
],
},
{
params: [
{ name: 'field', type: 'number' },
{ name: 'buckets', type: 'number', constantOnly: true },
{ name: 'startValue', type: 'number', constantOnly: true },
{ name: 'endValue', type: 'number', constantOnly: true },
],
returnType: 'number',
examples: ['from index | eval bs = bucket(bytes, 20, 25324, 74999)'],
},
],
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import type { ESQLCommand, ESQLCommandOption, ESQLFunction, ESQLMessage } from '@kbn/esql-ast';

export interface FunctionDefinition {
type: 'builtin' | 'agg' | 'eval';
type: 'builtin' | 'agg' | 'eval' | 'grouping';
ignoreAsSuggestion?: boolean;
name: string;
alias?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { statsAggregationFunctionDefinitions } from '../definitions/aggs';
import { builtinFunctions } from '../definitions/builtin';
import { commandDefinitions } from '../definitions/commands';
import { evalFunctionsDefinitions } from '../definitions/functions';
import { groupingFunctionDefinitions } from '../definitions/grouping';
import { getFunctionSignatures } from '../definitions/helpers';
import { chronoLiterals, timeLiterals } from '../definitions/literals';
import {
Expand Down Expand Up @@ -128,7 +129,11 @@ let commandLookups: Map<string, CommandDefinition> | undefined;
function buildFunctionLookup() {
if (!fnLookups) {
fnLookups = builtinFunctions
.concat(evalFunctionsDefinitions, statsAggregationFunctionDefinitions)
.concat(
evalFunctionsDefinitions,
statsAggregationFunctionDefinitions,
groupingFunctionDefinitions
)
.reduce((memo, def) => {
memo.set(def.name, def);
if (def.alias) {
Expand Down
Loading

0 comments on commit 5c69e1f

Please sign in to comment.