-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Obs ai assistant][ESQL] Visualizes a query #174677
Changes from 14 commits
0548517
9132a88
a60baff
e90f370
e59aa6c
ccbc3ae
09ff297
b6a37f1
08bd3f4
bba1b4e
df0c56a
2016b6a
e7a171a
704b8f5
2bb0c06
5628842
5e71490
60a1d40
1b4e2da
c8b3896
b5fe67b
e843fec
84e26ff
6b01204
4632fb1
cfcda31
ccd5810
536fce1
887603b
63ce42f
2d1e844
d0cca57
b1cd423
b9805c1
cd4b939
a72b140
2c5bd3b
4547567
7894fbd
4d69406
029a531
f874e16
d76002e
2a13505
dac6998
98fc8da
9f39185
d79102c
df07b85
66924a0
5705307
0d7c31b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* 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 type { Suggestion } from '../src/types'; | ||
|
||
export const currentSuggestionMock = { | ||
title: 'Heat map', | ||
hide: false, | ||
score: 0.6, | ||
previewIcon: 'heatmap', | ||
visualizationId: 'lnsHeatmap', | ||
visualizationState: { | ||
shape: 'heatmap', | ||
layerId: '46aa21fa-b747-4543-bf90-0b40007c546d', | ||
layerType: 'data', | ||
legend: { | ||
isVisible: true, | ||
position: 'right', | ||
type: 'heatmap_legend', | ||
}, | ||
gridConfig: { | ||
type: 'heatmap_grid', | ||
isCellLabelVisible: false, | ||
isYAxisLabelVisible: true, | ||
isXAxisLabelVisible: true, | ||
isYAxisTitleVisible: false, | ||
isXAxisTitleVisible: false, | ||
}, | ||
valueAccessor: '5b9b8b76-0836-4a12-b9c0-980c9900502f', | ||
xAccessor: '81e332d6-ee37-42a8-a646-cea4fc75d2d3', | ||
}, | ||
keptLayerIds: ['46aa21fa-b747-4543-bf90-0b40007c546d'], | ||
datasourceState: { | ||
layers: { | ||
'46aa21fa-b747-4543-bf90-0b40007c546d': { | ||
index: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', | ||
query: { | ||
esql: 'FROM kibana_sample_data_flights | keep Dest, AvgTicketPrice', | ||
}, | ||
columns: [ | ||
{ | ||
columnId: '81e332d6-ee37-42a8-a646-cea4fc75d2d3', | ||
fieldName: 'Dest', | ||
meta: { | ||
type: 'string', | ||
}, | ||
}, | ||
{ | ||
columnId: '5b9b8b76-0836-4a12-b9c0-980c9900502f', | ||
fieldName: 'AvgTicketPrice', | ||
meta: { | ||
type: 'number', | ||
}, | ||
}, | ||
], | ||
timeField: 'timestamp', | ||
}, | ||
}, | ||
indexPatternRefs: [], | ||
initialContext: { | ||
dataViewSpec: { | ||
id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', | ||
version: 'WzM1ODA3LDFd', | ||
title: 'kibana_sample_data_flights', | ||
timeFieldName: 'timestamp', | ||
sourceFilters: [], | ||
fields: { | ||
AvgTicketPrice: { | ||
count: 0, | ||
name: 'AvgTicketPrice', | ||
type: 'number', | ||
esTypes: ['float'], | ||
scripted: false, | ||
searchable: true, | ||
aggregatable: true, | ||
readFromDocValues: true, | ||
format: { | ||
id: 'number', | ||
params: { | ||
pattern: '$0,0.[00]', | ||
}, | ||
}, | ||
shortDotsEnable: false, | ||
isMapped: true, | ||
}, | ||
Dest: { | ||
count: 0, | ||
name: 'Dest', | ||
type: 'string', | ||
esTypes: ['keyword'], | ||
scripted: false, | ||
searchable: true, | ||
aggregatable: true, | ||
readFromDocValues: true, | ||
format: { | ||
id: 'string', | ||
}, | ||
shortDotsEnable: false, | ||
isMapped: true, | ||
}, | ||
timestamp: { | ||
count: 0, | ||
name: 'timestamp', | ||
type: 'date', | ||
esTypes: ['date'], | ||
scripted: false, | ||
searchable: true, | ||
aggregatable: true, | ||
readFromDocValues: true, | ||
format: { | ||
id: 'date', | ||
}, | ||
shortDotsEnable: false, | ||
isMapped: true, | ||
}, | ||
}, | ||
allowNoIndex: false, | ||
name: 'Kibana Sample Data Flights', | ||
}, | ||
fieldName: '', | ||
contextualFields: ['Dest', 'AvgTicketPrice'], | ||
query: { | ||
esql: 'FROM "kibana_sample_data_flights"', | ||
}, | ||
}, | ||
}, | ||
datasourceId: 'textBased', | ||
columns: 2, | ||
changeType: 'initial', | ||
} as Suggestion; | ||
drewdaemon marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* 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 { getLensAttributesFromSuggestion } from './get_lens_attributes'; | ||
import { AggregateQuery } from '@kbn/es-query'; | ||
import type { DataView } from '@kbn/data-views-plugin/public'; | ||
import { currentSuggestionMock } from '../__mocks__/suggestions_mock'; | ||
|
||
describe('getLensAttributesFromSuggestion', () => { | ||
const dataView = { | ||
id: `index-pattern-with-timefield-id`, | ||
title: `index-pattern-with-timefield-title`, | ||
fields: [], | ||
getFieldByName: jest.fn(), | ||
timeFieldName: '@timestamp', | ||
isPersisted: () => false, | ||
toSpec: () => ({}), | ||
} as unknown as DataView; | ||
const query: AggregateQuery = { esql: 'from foo | limit 10' }; | ||
|
||
it('should return correct attributes for given suggestion', () => { | ||
const lensAttrs = getLensAttributesFromSuggestion({ | ||
filters: [], | ||
query, | ||
dataView, | ||
suggestion: currentSuggestionMock, | ||
}); | ||
expect(lensAttrs).toEqual({ | ||
state: expect.objectContaining({ | ||
adHocDataViews: { | ||
'index-pattern-with-timefield-id': {}, | ||
}, | ||
}), | ||
references: [ | ||
{ | ||
id: 'index-pattern-with-timefield-id', | ||
name: 'textBasedLanguages-datasource-layer-suggestion', | ||
type: 'index-pattern', | ||
}, | ||
], | ||
title: currentSuggestionMock.title, | ||
visualizationType: 'lnsHeatmap', | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* 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 type { DataView } from '@kbn/data-views-plugin/public'; | ||
import type { AggregateQuery, Query, Filter } from '@kbn/es-query'; | ||
import type { Suggestion } from './types'; | ||
|
||
export const getLensAttributesFromSuggestion = ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ I can reuse it in Lens but I will do it on a follow up PR. I moved it on the package because othwerise it creates circular dependencies. |
||
filters, | ||
query, | ||
suggestion, | ||
dataView, | ||
}: { | ||
filters: Filter[]; | ||
query: Query | AggregateQuery; | ||
suggestion: Suggestion | undefined; | ||
dataView?: DataView; | ||
}) => { | ||
const suggestionDatasourceState = Object.assign({}, suggestion?.datasourceState); | ||
const suggestionVisualizationState = Object.assign({}, suggestion?.visualizationState); | ||
const datasourceStates = | ||
suggestion && suggestion.datasourceState | ||
? { | ||
[suggestion.datasourceId!]: { | ||
...suggestionDatasourceState, | ||
}, | ||
} | ||
: { | ||
formBased: {}, | ||
}; | ||
const visualization = suggestionVisualizationState; | ||
const attributes = { | ||
title: suggestion | ||
? suggestion.title | ||
: i18n.translate('visualizationUtils.config.suggestion.title', { | ||
defaultMessage: 'New suggestion', | ||
}), | ||
references: [ | ||
{ | ||
id: dataView?.id ?? '', | ||
name: `textBasedLanguages-datasource-layer-suggestion`, | ||
type: 'index-pattern', | ||
}, | ||
], | ||
state: { | ||
datasourceStates, | ||
filters, | ||
query, | ||
visualization, | ||
...(dataView && | ||
dataView.id && | ||
!dataView.isPersisted() && { | ||
adHocDataViews: { [dataView.id]: dataView.toSpec(false) }, | ||
}), | ||
}, | ||
visualizationType: suggestion ? suggestion.visualizationId : 'lnsXY', | ||
}; | ||
return attributes; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* 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 type { Ast } from '@kbn/interpreter'; | ||
import type { IconType } from '@elastic/eui/src/components/icon/icon'; | ||
|
||
/** | ||
* Indicates what was changed in this table compared to the currently active table of this layer. | ||
* * `initial` means the layer associated with this table does not exist in the current configuration | ||
* * `unchanged` means the table is the same in the currently active configuration | ||
* * `reduced` means the table is a reduced version of the currently active table (some columns dropped, but not all of them) | ||
* * `extended` means the table is an extended version of the currently active table (added one or multiple additional columns) | ||
* * `reorder` means the table columns have changed order, which change the data as well | ||
* * `layers` means the change is a change to the layer structure, not to the table | ||
*/ | ||
export type TableChangeType = | ||
| 'initial' | ||
| 'unchanged' | ||
| 'reduced' | ||
| 'extended' | ||
| 'reorder' | ||
| 'layers'; | ||
|
||
export interface Suggestion<T = unknown, V = unknown> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ Same as above. This can be reused in Lens and unified-histogram but I will do it on a follow up PR |
||
visualizationId: string; | ||
datasourceState?: V; | ||
datasourceId?: string; | ||
columns: number; | ||
score: number; | ||
title: string; | ||
visualizationState: T; | ||
previewExpression?: Ast | string; | ||
previewIcon: IconType; | ||
hide?: boolean; | ||
// flag to indicate if the visualization is incomplete | ||
incomplete?: boolean; | ||
changeType: TableChangeType; | ||
keptLayerIds: string[]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ I just moved this helper here as we are duplicating this code in many places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this function takes an Elasticsearch field type and translates it to a Kibana field type? If so, perhaps we could call this function something like
normalizeType
could mean practically anything. (I know you didn't name this function BTW—still thought the comment was worth making 🤷♂️ )There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is fair, I just moved it and didnt think to rename. I addressed it here e843fec