diff --git a/x-pack/plugins/lens/public/data_views_service/loader.ts b/x-pack/plugins/lens/public/data_views_service/loader.ts index b7af8b5258a70..97c04ee4a4211 100644 --- a/x-pack/plugins/lens/public/data_views_service/loader.ts +++ b/x-pack/plugins/lens/public/data_views_service/loader.ts @@ -35,6 +35,7 @@ export function convertDataViewIntoLensIndexPattern( // Convert the getters on the index pattern service into plain JSON const base = { name: field.name, + count: field.count, displayName: field.displayName, type: field.type, aggregatable: field.aggregatable, diff --git a/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx b/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx index 26c9da50aa8be..c00c74bedd421 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx @@ -345,6 +345,11 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ [core.uiSettings] ); + const popularFieldsLimit = useMemo( + () => core.uiSettings.get('fields:popularLimit'), + [core.uiSettings] + ); + const fieldListGroupedProps = useGroupedFields({ dataViewId: currentIndexPatternId, allFields, @@ -352,6 +357,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ dataViews, }, fieldsExistenceReader, + popularFieldsLimit: popularFieldsLimit ?? 0, isAffectedByGlobalFilter: Boolean(filters.length), onFilterField, onSupportedFieldFilter, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 03aed23f95237..53b7a3a22fb05 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -50,6 +50,7 @@ import { import { onDropForVisualization, shouldRemoveSource } from './buttons/drop_targets_utils'; import { getSharedActions } from './layer_actions/layer_actions'; import { FlyoutContainer } from './flyout_container'; +import { popularizeField } from '../../../utils'; // hide the random sampling settings from the UI const DISPLAY_RANDOM_SAMPLING_SETTINGS = false; @@ -118,6 +119,7 @@ export function LayerPanel( visualizationState, onChangeIndexPattern, core, + indexPatternService, } = props; const datasourceStates = useLensSelector(selectDatasourceStates); @@ -214,6 +216,18 @@ export function LayerPanel( ); } if (hasDropSucceeded) { + if (dropType === 'field_replace' || dropType === 'field_add') { + popularizeField( + source.indexPatternId as string, + source.id, + props.dataViewsService, + core.application.capabilities, + indexPatternService, + framePublicAPI.dataViews.indexPatterns, + datasourceStates, + props.datasourceMap + ); + } activeVisualization.onDrop = activeVisualization.onDrop?.bind(activeVisualization); updateVisualization( @@ -249,6 +263,9 @@ export function LayerPanel( activeVisualization, updateVisualization, props, + core.application.capabilities, + datasourceStates, + indexPatternService, ]); const isDimensionPanelOpen = Boolean(activeId); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index c4a2d77c30bab..d0482eec0c875 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { IndexPatternServiceAPI } from '../../../data_views_service/service'; @@ -23,6 +24,7 @@ export interface ConfigPanelWrapperProps { core: DatasourceDimensionEditorProps['core']; indexPatternService: IndexPatternServiceAPI; uiActions: UiActionsStart; + dataViewsService: DataViewsPublicPluginStart; } export interface LayerPanelProps { @@ -31,6 +33,7 @@ export interface LayerPanelProps { activeVisualization: Visualization; framePublicAPI: FramePublicAPI; core: DatasourceDimensionEditorProps['core']; + dataViewsService: DataViewsPublicPluginStart; } export interface LayerDatasourceDropProps { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 4100e25da7667..4f63a4a6045ce 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -31,6 +31,7 @@ import { import type { LensInspector } from '../../lens_inspector_service'; import { ErrorBoundary, showMemoizedErrorNotification } from '../../lens_ui_errors'; import { IndexPatternServiceAPI } from '../../data_views_service/service'; +import { popularizeField } from '../../utils'; export interface EditorFrameProps { datasourceMap: DatasourceMap; @@ -85,11 +86,30 @@ export function EditorFrame(props: EditorFrameProps) { (field) => { const suggestion = getSuggestionForField.current!(field); if (suggestion) { + popularizeField( + field.indexPatternId, + field.id, + props.plugins.dataViews, + props.core.application.capabilities, + props.indexPatternService, + framePublicAPI.dataViews.indexPatterns, + datasourceStates, + datasourceMap + ); trackUiCounterEvents('drop_onto_workspace'); switchToSuggestion(dispatchLens, suggestion, { clearStagedPreview: true }); } }, - [getSuggestionForField, dispatchLens] + [ + getSuggestionForField, + dispatchLens, + props.plugins.dataViews, + props.core.application.capabilities, + props.indexPatternService, + framePublicAPI.dataViews.indexPatterns, + datasourceStates, + datasourceMap, + ] ); const onError = useCallback((error: Error) => { @@ -139,6 +159,7 @@ export function EditorFrame(props: EditorFrameProps) { framePublicAPI={framePublicAPI} uiActions={props.plugins.uiActions} indexPatternService={props.indexPatternService} + dataViewsService={props.plugins.dataViews} /> ) @@ -156,6 +177,7 @@ export function EditorFrame(props: EditorFrameProps) { visualizationMap={visualizationMap} framePublicAPI={framePublicAPI} getSuggestionForField={getSuggestionForField.current} + indexPatternService={props.indexPatternService} /> ) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index e5b4495b2a7a5..3dea623127b1e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -30,13 +30,14 @@ import type { ExpressionRenderError, ReactExpressionRendererType, } from '@kbn/expressions-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import type { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; import type { Datatable } from '@kbn/expressions-plugin/public'; import { DropIllustration } from '@kbn/chart-icons'; import { trackUiCounterEvents } from '../../../lens_ui_telemetry'; -import { getSearchWarningMessages } from '../../../utils'; +import { getSearchWarningMessages, popularizeField } from '../../../utils'; import { FramePublicAPI, isLensBrushEvent, @@ -48,7 +49,7 @@ import { Suggestion, DatasourceLayers, } from '../../../types'; -import { DragDrop, DragContext, DragDropIdentifier } from '../../../drag_drop'; +import { DragDrop, DragContext, DragDropIdentifier, DragContextState } from '../../../drag_drop'; import { switchToSuggestion } from '../suggestion_helpers'; import { buildExpression } from '../expression_helpers'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; @@ -84,6 +85,7 @@ import { import type { LensInspector } from '../../../lens_inspector_service'; import { inferTimeField, DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../utils'; import { setChangesApplied } from '../../../state_management/lens_slice'; +import { IndexPatternServiceAPI } from '../../../data_views_service/service'; export interface WorkspacePanelProps { visualizationMap: VisualizationMap; @@ -91,9 +93,14 @@ export interface WorkspacePanelProps { framePublicAPI: FramePublicAPI; ExpressionRenderer: ReactExpressionRendererType; core: CoreStart; - plugins: { uiActions?: UiActionsStart; data: DataPublicPluginStart }; + plugins: { + uiActions?: UiActionsStart; + data: DataPublicPluginStart; + dataViews: DataViewsPublicPluginStart; + }; getSuggestionForField: (field: DragDropIdentifier) => Suggestion | undefined; lensInspector: LensInspector; + indexPatternService: IndexPatternServiceAPI; } interface WorkspaceState { @@ -137,7 +144,11 @@ export const WorkspacePanel = React.memo(function WorkspacePanel(props: Workspac ); return ( - + ); }); @@ -151,8 +162,11 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ ExpressionRenderer: ExpressionRendererComponent, suggestionForDraggedField, lensInspector, + indexPatternService, + dragDropCtx, }: Omit & { suggestionForDraggedField: Suggestion | undefined; + dragDropCtx: DragContextState; }) { const dispatchLens = useLensDispatch(); const isFullscreen = useLensSelector(selectIsFullscreenDatasource); @@ -475,11 +489,31 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ }, [expressionExists, localState.expressionBuildError]); const onDrop = useCallback(() => { - if (suggestionForDraggedField) { + if (suggestionForDraggedField && dragDropCtx.dragging) { + popularizeField( + dragDropCtx.dragging.indexPatternId as string, + dragDropCtx.dragging.id, + plugins.dataViews, + core.application.capabilities, + indexPatternService, + framePublicAPI.dataViews.indexPatterns, + datasourceStates, + datasourceMap + ); trackUiCounterEvents('drop_onto_workspace'); switchToSuggestion(dispatchLens, suggestionForDraggedField, { clearStagedPreview: true }); } - }, [suggestionForDraggedField, dispatchLens]); + }, [ + suggestionForDraggedField, + dragDropCtx.dragging, + plugins.dataViews, + core.application.capabilities, + dispatchLens, + datasourceStates, + indexPatternService, + framePublicAPI.dataViews.indexPatterns, + datasourceMap, + ]); const IS_DARK_THEME = core.uiSettings.get('theme:darkMode'); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index f37f0b8369cd8..91d8cecbd53c1 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -640,7 +640,10 @@ export type DatasourceDimensionEditorProps = DatasourceDimensionPro forceRender?: boolean; } >; - core: Pick; + core: Pick< + CoreStart, + 'http' | 'notifications' | 'uiSettings' | 'overlays' | 'theme' | 'application' + >; dateRange: DateRange; dimensionGroups: VisualizationDimensionGroupConfig[]; toggleFullscreen: () => void; diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 16ad6a026851d..4401f4641f361 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -10,8 +10,12 @@ import moment from 'moment-timezone'; import type { Serializable } from '@kbn/utility-types'; import type { TimefilterContract } from '@kbn/data-plugin/public'; -import type { IUiSettingsClient, SavedObjectReference } from '@kbn/core/public'; -import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public'; +import type { Capabilities, IUiSettingsClient, SavedObjectReference } from '@kbn/core/public'; +import type { + DataView, + DataViewsContract, + DataViewsPublicPluginStart, +} from '@kbn/data-views-plugin/public'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import { BrushTriggerEvent, ClickTriggerEvent } from '@kbn/charts-plugin/public'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; @@ -328,3 +332,48 @@ export const getSearchWarningMessages = ( return [...warningsMap.values()].flat(); }; + +export async function popularizeField( + indexPatternId: string, + fieldName: string, + dataViewsService: DataViewsPublicPluginStart, + capabilities: Capabilities, + indexPatternService: IndexPatternServiceAPI, + indexPatternsCache: IndexPatternMap, + datasourceStates: DatasourceStates, + datasourceMap: DatasourceMap +) { + if (!indexPatternId || !capabilities?.indexPatterns?.save) return; + const dataView = await dataViewsService.get(indexPatternId); + const field = dataView.fields.getByName(fieldName); + if (!field) { + return; + } + + field.count++; + + const refreshIndexPatternsListArgs = { + activeDatasources: Object.keys(datasourceStates).reduce( + (acc, datasourceId) => ({ + ...acc, + [datasourceId]: datasourceMap[datasourceId], + }), + {} + ), + indexPatternId, + indexPatternService, + indexPatternsCache, + }; + + if (!dataView.isPersisted()) { + refreshIndexPatternsList(refreshIndexPatternsListArgs); + return; + } + + // Catch 409 errors caused by user adding columns in a higher frequency that the changes can be persisted to Elasticsearch + try { + await dataViewsService.updateSavedObject(dataView, 0, true); + refreshIndexPatternsList(refreshIndexPatternsListArgs); + // eslint-disable-next-line no-empty + } catch {} +}