Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions x-pack/platform/plugins/shared/lens/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ dependsOn:
- '@kbn/as-code-shared-schemas'
- '@kbn/as-code-shared-telemetry'
- '@kbn/core-http-browser'
- '@kbn/use-observable'
tags:
- plugin
- prod
Expand Down
Copy link
Copy Markdown
Contributor Author

@mariairiartef mariairiartef May 5, 2026

Choose a reason for hiding this comment

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

Nit

I've noticed that when you run the query, the "ES|QL Query Results" section is collapsed:

Screen.Recording.2026-05-05.at.16.39.23.mov

@teresaalvarezsoler, do you think we should do the same when the ES|QL query results section updates on date / filters change?

Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ import { css } from '@emotion/react';
import { EuiFlexItem, useEuiTheme } from '@elastic/eui';
import type { AggregateQuery, Query } from '@kbn/es-query';
import { isOfAggregateQueryType } from '@kbn/es-query';
import type { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common';
import { useFetchContext } from '@kbn/presentation-publishing';
import type { CoreStart, IUiSettingsClient } from '@kbn/core/public';
import { isEqual } from 'lodash';
import type { MutableRefObject } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ESQLLangEditor, useESQLQueryStats } from '@kbn/esql/public';
import { type ESQLControlVariable, type ESQLQueryStats } from '@kbn/esql-types';
import { i18n } from '@kbn/i18n';
import React from 'react';
import type { DataViewSpec } from '@kbn/data-views-plugin/common';
import type { Simplify } from '@kbn/chart-expressions-common';
import { useObservable } from '@kbn/use-observable';
import { EMPTY } from 'rxjs';
import { useCurrentAttributes } from '../../../app_plugin/shared/edit_on_the_fly/use_current_attributes';
import { useESQLEditorContext } from './esql_editor_context';
import { getActiveDataFromDatatable } from '../../../state_management/shared_logic';
Expand All @@ -29,6 +30,7 @@ import {
useLensDispatch,
useLensSelector,
selectCanEditTextBasedQuery,
selectSearchSessionId,
} from '../../../state_management';
import type { ESQLDataGridAttrs } from '../../../app_plugin/shared/edit_on_the_fly/helpers';
import { getSuggestions } from '../../../app_plugin/shared/edit_on_the_fly/helpers';
Expand Down Expand Up @@ -96,6 +98,10 @@ export function ESQLEditor({
const { visualizationMap, datasourceMap } = useEditorFrameService();
const { visualization } = useLensSelector((state) => state.lens);
const canEditTextBasedQuery = useLensSelector(selectCanEditTextBasedQuery);
// Updated when the workspace kicks off a new search (manual refresh, auto-refresh,
// or when chart requests run under a new session). Used as an effect dependency to
// re-fetch the ES|QL results grid for the last submitted query.
const searchSessionId = useLensSelector(selectSearchSessionId);

const [errors, setErrors] = useState<Error[]>([]);
const [submittedQuery, setSubmittedQuery] = useState<AggregateQuery | Query>(
Expand All @@ -119,42 +125,48 @@ export function ESQLEditor({
const currentAttributesRef = useRef(currentAttributes);
currentAttributesRef.current = currentAttributes;

const adHocDataViews =
attributes && attributes.state.adHocDataViews
? Object.values(attributes.state.adHocDataViews)
: Object.values(framePublicAPI.dataViews.indexPatterns).map((index) => index.spec);
const adHocDataViews = useMemo(() => {
if (attributes && attributes.state.adHocDataViews) {
return Object.values(attributes.state.adHocDataViews);
}
return Object.values(framePublicAPI.dataViews.indexPatterns).map((index) => index.spec);
}, [attributes, framePublicAPI.dataViews.indexPatterns]);

const previousAdapters = useRef<Partial<DefaultInspectorAdapters> | undefined>(lensAdapters);
const lensAdaptersRef = useRef(lensAdapters);
lensAdaptersRef.current = lensAdapters;

// Avoids duplicating the first grid load
const isInitialRenderRef = useRef(true);

const submittedQueryRef = useRef(submittedQuery);
submittedQueryRef.current = submittedQuery;

const { esqlVariables } = useFetchContext({ uuid: panelId, parentApi });
const esqlQueryStats = useESQLQueryStats(isTextBasedLanguage, lensAdapters?.requests);

const dispatch = useLensDispatch();

// Update activeData and column limit indicator when chart data finishes loading
const isDataLoading = useObservable(dataLoading$ ?? EMPTY);

useEffect(() => {
const s = dataLoading$?.subscribe((isDataLoading) => {
// go thru only when the loading is complete
if (isDataLoading) {
return;
}
const activeData = getActiveDataFromDatatable(
layerId,
previousAdapters.current?.tables?.tables
);
const table = activeData?.[layerId];
if (isDataLoading !== false) {
return;
}

if (table) {
// there are cases where a query can return a big amount of columns
// at this case we don't suggest all columns in a table but the first `MAX_NUM_OF_COLUMNS`
setSuggestsLimitedColumns(table.columns.length >= MAX_NUM_OF_COLUMNS);
}
const activeData = getActiveDataFromDatatable(layerId, lensAdaptersRef.current?.tables?.tables);

if (Object.keys(activeData).length > 0) {
dispatch(onActiveDataChange({ activeData }));
}
});
return () => s?.unsubscribe();
}, [dataLoading$, dispatch, layerId]);
const table = activeData?.[layerId];
if (table) {
// there are cases where a query can return a big amount of columns
// at this case we don't suggest all columns in a table but the first `MAX_NUM_OF_COLUMNS`
setSuggestsLimitedColumns(table.columns.length >= MAX_NUM_OF_COLUMNS);
}

if (Object.keys(activeData).length > 0) {
dispatch(onActiveDataChange({ activeData }));
}
}, [isDataLoading, dispatch, layerId]);

const runQuery = useCallback(
async (q: AggregateQuery, abortController?: AbortController, shouldUpdateAttrs?: boolean) => {
Expand Down Expand Up @@ -215,6 +227,54 @@ export function ESQLEditor({
});
}, [query, submittedQuery, errors.length, onTextBasedQueryStateChange]);

// Refresh the ES|QL results table for the last submitted query when inputs to the preview
// request change without the user submitting again.
useEffect(() => {
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
// Skip the initial render, the grid is populated by useInitializeChart → runQuery
if (isInitialRenderRef.current) {
isInitialRenderRef.current = false;
return;
}

const lastSubmittedQuery = submittedQueryRef.current;
if (!isOfAggregateQueryType(lastSubmittedQuery)) {
return;
}

const abortController = new AbortController();

getSuggestions(
lastSubmittedQuery,
data,
http,
uiSettings,
datasourceMap,
visualizationMap,
adHocDataViews,
undefined,
abortController,
setDataGridAttrs,
esqlVariables,
false,
currentAttributesRef.current
).catch(() => {
// The chart itself will surface query errors via its own error handling path
});

return () => {
abortController.abort();
};
}, [
searchSessionId,
esqlVariables,
data,
http,
uiSettings,
datasourceMap,
visualizationMap,
adHocDataViews,
]);

// Early exit if it's not in TextBased mode or the editor should be hidden
if (!isTextBasedLanguage || !canEditTextBasedQuery || !isOfAggregateQueryType(query)) {
return null;
Expand Down
3 changes: 2 additions & 1 deletion x-pack/platform/plugins/shared/lens/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@
"@kbn/esql-language",
"@kbn/as-code-shared-schemas",
"@kbn/as-code-shared-telemetry",
"@kbn/core-http-browser"
"@kbn/core-http-browser",
"@kbn/use-observable"
],
"exclude": ["target/**/*"]
}
Loading