Skip to content
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

lens esql generation #196049

Open
wants to merge 102 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
6a56775
lens esql generation
ppisljar Oct 14, 2024
02b4ce9
handle missing options
ppisljar Oct 14, 2024
ca29b48
client side auto calculation
ppisljar Oct 14, 2024
6f2d487
more features
ppisljar Oct 17, 2024
3e33117
cleanup
ppisljar Oct 17, 2024
cd2908e
add advanced setting and basic tests
ppisljar Oct 22, 2024
31620f7
basic input sanitizaton
ppisljar Oct 22, 2024
e181929
fix error
ppisljar Oct 22, 2024
5c12195
update kibana auto bucketing strategy
ppisljar Oct 24, 2024
6428efc
use ENABLE_ESQL setting
ppisljar Oct 24, 2024
a097d5d
update sanitization
ppisljar Oct 24, 2024
9854348
Merge branch 'main' into poc/lensesql
ppisljar Oct 24, 2024
1c310e8
fix sanitization
ppisljar Oct 24, 2024
8733660
Merge branch 'poc/lensesql' of github.com:ppisljar/kibana into poc/le…
ppisljar Oct 24, 2024
1ed9574
field formatters
ppisljar Oct 24, 2024
7ae6eb7
Merge branch 'main' into poc/lensesql
ppisljar Oct 29, 2024
5d2b96d
Merge branch 'main' into poc/lensesql
ppisljar Oct 30, 2024
8c3258b
labels and formatters
ppisljar Nov 4, 2024
f07ab43
add feature flag
ppisljar Nov 12, 2024
e1e57b8
cleanup
ppisljar Nov 12, 2024
3dcccc5
cleanup
ppisljar Nov 12, 2024
2cc02b9
Merge branch 'main' into poc/lensesql
ppisljar Nov 12, 2024
1f0e333
fix test
ppisljar Nov 12, 2024
744fce4
Merge branch 'main' into poc/lensesql
ppisljar Nov 12, 2024
f50288f
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 12, 2024
eb97bfa
Merge branch 'main' into poc/lensesql
ppisljar Nov 13, 2024
047b62f
fix
ppisljar Nov 13, 2024
2e4e915
toggle flag (revert before merge)
ppisljar Nov 13, 2024
22a25a4
handle timezone
ppisljar Nov 13, 2024
b99c96e
fix field format handling
ppisljar Nov 13, 2024
788921a
fix field format handling
ppisljar Nov 19, 2024
3d19a0c
jest test
ppisljar Nov 19, 2024
9e3bf36
esql total hits
ppisljar Nov 19, 2024
ca50dbb
Merge branch 'main' into poc/lensesql
ppisljar Nov 19, 2024
219d137
Merge branch 'main' into poc/lensesql
ppisljar Nov 20, 2024
e964d8e
force DSL in discover (and other places with unified histogram)
ppisljar Nov 26, 2024
7f5b0d4
force DSL in discover (and other places with unified histogram)
ppisljar Nov 26, 2024
1f0b622
force DSL in discover (and other places with unified histogram)
ppisljar Nov 27, 2024
f7e45b6
merge main
ppisljar Nov 27, 2024
f83b14c
new embeddable
ppisljar Nov 27, 2024
c5ce419
Merge branch 'main' into poc/lensesql
ppisljar Nov 27, 2024
7ccff61
fix types
ppisljar Nov 27, 2024
e40f3c7
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 27, 2024
d14e60e
...
ppisljar Nov 28, 2024
2d9721c
dont add time filter if index has no timefield
ppisljar Dec 2, 2024
a33acf9
Merge branch 'main' into poc/lensesql
ppisljar Dec 2, 2024
1282dff
disable for formulas
ppisljar Dec 2, 2024
dd8b573
date histogram format scaling
ppisljar Dec 3, 2024
a66aac5
disable for time scaling
ppisljar Dec 3, 2024
49994ca
fix types
ppisljar Dec 3, 2024
71aeea7
esql filtering
ppisljar Dec 5, 2024
5fe3d49
Merge branch 'main' into poc/lensesql
ppisljar Dec 9, 2024
a00d274
...
ppisljar Dec 9, 2024
a121f52
disable for search sessions
ppisljar Dec 9, 2024
4bec564
merge main
ppisljar Dec 9, 2024
6797280
Merge branch 'main' into poc/lensesql
ppisljar Dec 9, 2024
faa4507
...
ppisljar Dec 9, 2024
68a170f
Merge branch 'poc/lensesql' of github.com:ppisljar/kibana into poc/le…
ppisljar Dec 9, 2024
2df80d1
...
ppisljar Dec 10, 2024
2af476a
handle formatting for count of records
ppisljar Dec 10, 2024
b34215b
disable for runtime fields
ppisljar Dec 10, 2024
6e3059b
Merge branch 'main' into poc/lensesql
ppisljar Dec 10, 2024
93cd513
Merge branch 'main' into poc/lensesql
ppisljar Dec 10, 2024
b0449bb
Merge branch 'main' into poc/lensesql
ppisljar Dec 10, 2024
a64c6df
Revert "toggle flag (revert before merge)"
ppisljar Dec 10, 2024
1355380
rebase on main
ppisljar Dec 11, 2024
df614ab
fix merge main
ppisljar Dec 11, 2024
4374d61
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 11, 2024
a1fd88d
fix merge main
ppisljar Dec 11, 2024
c2e19cb
merge main
ppisljar Dec 19, 2024
f8db55c
merge main
ppisljar Dec 19, 2024
f683a80
merge main
ppisljar Dec 20, 2024
c17eb1c
merge main
ppisljar Dec 20, 2024
2ab401d
Merge branch 'main' into poc/lensesql
ppisljar Jan 6, 2025
a603866
Merge branch 'main' into poc/lensesql
ppisljar Jan 7, 2025
9d8b116
Merge branch 'main' into poc/lensesql
stratoula Jan 8, 2025
cb25122
Apply suggestions from code review
ppisljar Jan 8, 2025
1772aa7
merge main
ppisljar Jan 8, 2025
ed49cd9
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Jan 8, 2025
003a98c
update based on review
ppisljar Jan 9, 2025
a4adfa7
Update src/platform/plugins/shared/data/public/actions/filters/create…
ppisljar Jan 9, 2025
9bc39cd
add filter creation test
ppisljar Jan 9, 2025
1102f5e
more tests
ppisljar Jan 9, 2025
b30c3ce
Merge branch 'poc/lensesql' of github.com:ppisljar/kibana into poc/le…
ppisljar Jan 9, 2025
87e7f7b
more tests
ppisljar Jan 9, 2025
fc03c86
fix
ppisljar Jan 9, 2025
b5bb6f4
fix for timezone
ppisljar Jan 9, 2025
3dfd811
add test
ppisljar Jan 9, 2025
9fae536
disable for ranges
ppisljar Jan 9, 2025
fadb2f8
fix bug with datehistogram
ppisljar Jan 13, 2025
895a92d
Merge branch 'main' into poc/lensesql
ppisljar Jan 13, 2025
7dfebfc
fix test
ppisljar Jan 13, 2025
45f221d
pass used_interval
ppisljar Jan 15, 2025
c9f594c
fix filtering
ppisljar Jan 15, 2025
6933945
fix filtering
ppisljar Jan 20, 2025
cfeaa80
Merge branch 'main' into poc/lensesql
ppisljar Jan 20, 2025
2a44aaa
Apply suggestions from code review
ppisljar Jan 21, 2025
d0d76e8
apply review feedback
ppisljar Jan 22, 2025
c780d9f
apply review feedback
ppisljar Jan 22, 2025
aa58127
apply review feedback
ppisljar Jan 22, 2025
8766331
Merge branch 'main' into poc/lensesql
ppisljar Jan 22, 2025
317c93b
apply review feedback
ppisljar Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export * from './lib/cidr_mask';
export * from './lib/date_range';
export * from './lib/ip_range';
export * from './lib/time_buckets/calc_auto_interval';
export { TimeBuckets } from './lib/time_buckets';
export { TimeBuckets, convertDurationToNormalizedEsInterval } from './lib/time_buckets';
export * from './migrate_include_exclude_format';
export * from './range_fn';
export * from './range';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
*/

export { TimeBuckets } from './time_buckets';
export { convertDurationToNormalizedEsInterval } from './calc_es_interval';
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { zipObject } from 'lodash';
import { catchError, defer, map, Observable, switchMap, tap, throwError } from 'rxjs';
import { buildEsQuery, type Filter } from '@kbn/es-query';
import type { ESQLSearchParams, ESQLSearchResponse } from '@kbn/es-types';
import DateMath from '@kbn/datemath';
import { getEsQueryConfig } from '../../es_query';
import { getTime } from '../../query';
import {
Expand Down Expand Up @@ -328,6 +329,13 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
);
const indexPattern = getIndexPatternFromESQLQuery(query);

const appliedTimeRange = input?.timeRange
? {
from: DateMath.parse(input.timeRange.from),
to: DateMath.parse(input.timeRange.to),
}
: undefined;

const allColumns =
(body.all_columns ?? body.columns)?.map(({ name, type }) => ({
id: name,
Expand All @@ -338,7 +346,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
sourceParams:
type === 'date'
? {
appliedTimeRange: input?.timeRange,
appliedTimeRange,
params: {},
indexPattern,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ describe('brushEvent', () => {
name: '1',
meta: {
type: 'date',
sourceParams: {},
},
},
],
Expand Down Expand Up @@ -247,5 +248,20 @@ describe('brushEvent', () => {
expect(rangeFilter.query.range['1']).toHaveProperty('format', 'strict_date_optional_time');
}
});

test('for column with different name than source field', async () => {
const rangeBegin = JAN_01_2014;
const rangeEnd = rangeBegin + DAY_IN_MS;
esqlEventContext.range = [rangeBegin, rangeEnd];
esqlEventContext.table.columns[0].meta!.sourceParams!.sourceField = 'time';
esqlEventContext.table.columns[0].name = 'time over 12h';

const filter = await createFiltersFromRangeSelectAction(esqlEventContext);

expect(filter).toBeDefined();
expect(filter.length).toEqual(1);
expect(filter[0].query).toBeDefined();
expect(filter[0].query!.range.time).toBeDefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export interface RangeSelectDataContext {
const getParameters = async (event: RangeSelectDataContext) => {
const column: Record<string, any> = event.table.columns[event.column];
// Handling of the ES|QL datatable
if (isOfAggregateQueryType(event.query)) {
if (isOfAggregateQueryType(event.query) || event.table.meta?.type === 'es_ql') {
const field = new DataViewField({
name: column.name,
name: column.meta?.sourceParams?.sourceField || column.name,
type: column.meta?.type ?? 'unknown',
esTypes: column.meta?.esType ? ([column.meta.esType] as string[]) : undefined,
searchable: true,
Expand All @@ -43,8 +43,14 @@ const getParameters = async (event: RangeSelectDataContext) => {
};
}
if (column.meta && 'sourceParams' in column.meta) {
const { indexPatternId, ...aggConfigs } = column.meta.sourceParams;
const { sourceField, ...aggConfigs } = column.meta.sourceParams;
const indexPatternId =
column.meta.sourceParams.indexPatternId || column.meta.sourceParams.indexPattern;
const indexPattern = await getIndexPatterns().get(indexPatternId);
// if (event.table.meta?.type === 'es_ql') {
// const field = indexPattern.fields.getByName(sourceField);
// return { field, indexPattern };
// }
const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [
aggConfigs as AggConfigSerialized,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { setIndexPatterns, setSearchService } from '../../services';
import {
createFiltersFromValueClickAction,
appendFilterToESQLQueryFromValueClickAction,
createFilterESQL,
} from './create_filters_from_value_click';
import { FieldFormatsGetConfigFn, BytesFormat } from '@kbn/field-formats-plugin/common';
import { RangeFilter } from '@kbn/es-query';
import { Datatable } from '@kbn/expressions-plugin/common';

const mockField = {
name: 'bytes',
Expand Down Expand Up @@ -105,6 +107,118 @@ describe('createFiltersFromClickEvent', () => {
expect(filters.length).toEqual(1);
});
});

describe('createFilterESQL', () => {
let table: Datatable;

beforeEach(() => {
table = {
type: 'datatable',
columns: [
{
name: 'test',
id: '1-1',
meta: {
type: 'number',
sourceParams: {
sourceField: 'bytes',
},
},
},
],
rows: [
{
'1-1': '2048',
},
],
};
});

test('ignores event when sourceField is missing', async () => {
table.columns[0].meta.sourceParams = {};
const filter = await createFilterESQL(table, 0, 0);

expect(filter).toBeUndefined();
});

test('ignores event when value for rows is not provided', async () => {
table.rows[0]['1-1'] = null;
const filter = await createFilterESQL(table, 0, 0);

expect(filter).toBeUndefined();
});

test('handles an event when operation type is a date histogram', async () => {
(table.columns[0].meta.sourceParams as any).operationType = 'date_histogram';
const filter = await createFilterESQL(table, 0, 0);

expect(filter).toMatchInlineSnapshot(`
Array [
Object {
"meta": Object {
"field": "bytes",
"index": "2048",
"params": Object {},
},
"query": Object {
"range": Object {
"bytes": Object {
"format": NaN,
"gte": 2048,
"lt": 2048,
},
},
},
},
]
`);
});

test('handles an event when operation type is histogram', async () => {
(table.columns[0].meta.sourceParams as any).operationType = 'histogram';
const filter = await createFilterESQL(table, 0, 0);

expect(filter).toMatchInlineSnapshot(`
Array [
Object {
"meta": Object {
"field": "bytes",
"index": "2048",
"params": Object {},
},
"query": Object {
"range": Object {
"bytes": Object {
"gte": 2048,
"lt": 2048,
},
},
},
},
]
`);
});

test('handles an event when operation type is not date histogram', async () => {
const filter = await createFilterESQL(table, 0, 0);

expect(filter).toMatchInlineSnapshot(`
Array [
Object {
"meta": Object {
"index": undefined,
},
"query": Object {
"exists": Object {
"field": "bytes",
},
},
},
]
`);
});
});

describe('appendFilterToESQLQueryFromValueClickAction', () => {
let dataPoints: Parameters<typeof appendFilterToESQLQueryFromValueClickAction>[0]['data'];
beforeEach(() => {
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clicking on a metric with count(*) creates a wrong filter

image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in c9f594c

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed now, the click is not enabled if the metric is showing count of records

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be enabled here though right?

image

I am clicking the metric but I dont get a filter

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import _ from 'lodash';
import { Datatable } from '@kbn/expressions-plugin/public';
import { Datatable, isSourceParamsESQL } from '@kbn/expressions-plugin/public';
import {
compareFilters,
COMPARE_ALL_OPTIONS,
Expand All @@ -17,13 +17,17 @@ import {
type AggregateQuery,
} from '@kbn/es-query';
import { appendWhereClauseToESQLQuery } from '@kbn/esql-utils';
import {
buildSimpleExistFilter,
buildSimpleNumberRangeFilter,
} from '@kbn/es-query/src/filters/build_filters';
import { getIndexPatterns, getSearchService } from '../../services';
import { AggConfigSerialized } from '../../../common/search/aggs';
import { mapAndFlattenFilters } from '../../query';

interface ValueClickDataContext {
data: Array<{
table: Pick<Datatable, 'rows' | 'columns'>;
table: Pick<Datatable, 'rows' | 'columns' | 'meta'>;
column: number;
row: number;
value: any;
Expand Down Expand Up @@ -129,6 +133,51 @@ export const createFilter = async (
return filter;
};

export const createFilterESQL = async (
table: Pick<Datatable, 'rows' | 'columns'>,
columnIndex: number,
rowIndex: number
) => {
const column = table?.columns?.[columnIndex];
if (
!column?.meta?.sourceParams?.sourceField ||
column.meta.sourceParams?.sourceField === '___records___'
) {
return [];
}
const sourceParams = column.meta.sourceParams;
if (!isSourceParamsESQL(sourceParams)) {
return [];
}
const { indexPattern, sourceField, operationType, interval } = sourceParams;

const value = rowIndex > -1 ? table.rows[rowIndex][column.id] : null;
if (value == null) {
return [];
}

const filters: Filter[] = [];

if (['date_histogram', 'histogram'].includes(operationType)) {
filters.push(
buildSimpleNumberRangeFilter(
sourceField,
{
gte: value,
lt: value + interval,
...(operationType === 'date_hisotgram' ? { format: 'strict_date_optional_time' } : {}),
},
value,
indexPattern
)
);
} else {
filters.push(buildSimpleExistFilter(sourceField, indexPattern));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we're always going to create either a range or an exists filter when clicking on a value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what is currently supported, on aggconfigs we also do ip range filters, ip prefix, terms and multiterms filters, but this is not yet supported on esql level at the moment (or by this PR)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you let me know how to get into a situation in this PR where something other than an exists filter should be created? I can't seem to set up any Lens configurations that actually send the ES|QL query instead of the aggs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I think you wont find any Lukas, I was also trying to test this scenario

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lukas go to advanced settings and set time zone to UTC
then time charts should also work (basic ones) and date range should be updated when filtering

}

return filters;
};

/** @public */
export const createFiltersFromValueClickAction = async ({
data,
Expand All @@ -141,7 +190,10 @@ export const createFiltersFromValueClickAction = async ({
.filter((point) => point)
.map(async (val) => {
const { table, column, row } = val;
const filter: Filter[] = (await createFilter(table, column, row)) || [];
const filter =
table.meta?.type === 'es_ql'
? await createFilterESQL(table, column, row)
ppisljar marked this conversation as resolved.
Show resolved Hide resolved
: (await createFilter(table, column, row)) || [];
if (filter) {
filter.forEach((f) => {
if (negate) {
Expand Down
1 change: 1 addition & 0 deletions src/platform/plugins/shared/data/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
getIndexPatternFromFilter,
} from './query';

export { convertIntervalToEsInterval } from '../common/search/aggs/buckets/lib/time_buckets/calc_es_interval';
/**
* Exporters (CSV)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ export interface DatatableColumnMeta {
sourceParams?: SerializableRecord;
}

interface SourceParamsESQL extends Record<string, unknown> {
indexPattern: string;
sourceField: string;
operationType: string;
interval: number;
}

export function isSourceParamsESQL(obj: Record<string, unknown>): obj is SourceParamsESQL {
return (
obj &&
typeof obj.indexPattern === 'string' &&
typeof obj.sourceField === 'string' &&
typeof obj.operationType === 'string' &&
typeof obj.interval === 'number'
);
}

/**
* This type represents the shape of a column in a `Datatable`.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/platform/plugins/shared/expressions/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,5 @@ export {
parseExpression,
createDefaultInspectorAdapters,
} from '../common';

export { isSourceParamsESQL } from '../common/expression_types';
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ describe('map_to_columns', () => {
);

expect(result.columns).toStrictEqual([
{ id: 'a', name: 'A', meta: { type: 'number', field: undefined, params: undefined } },
{ id: 'b', name: 'B', meta: { type: 'number', field: undefined, params: undefined } },
{ id: 'a', name: 'A', meta: { type: 'number', sourceParams: {} } },
{ id: 'b', name: 'B', meta: { type: 'number', sourceParams: {} } },
]);

expect(result.rows).toStrictEqual([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { OriginalColumn, MapToColumnsExpressionFunction } from './types';

function getColumnName(originalColumn: OriginalColumn, newColumn: DatatableColumn) {
if (originalColumn?.operationType === 'date_histogram') {
const fieldName = originalColumn.sourceField;
const fieldName = originalColumn.sourceField as string;

// HACK: This is a hack, and introduces some fragility into
// column naming. Eventually, this should be calculated and
Expand Down
Loading