Skip to content

[Discover] [Group By] Support group by fetching across tabs#251154

Merged
davismcphee merged 21 commits into
elastic:mainfrom
davismcphee:discover-group-by-fetching
Feb 18, 2026
Merged

[Discover] [Group By] Support group by fetching across tabs#251154
davismcphee merged 21 commits into
elastic:mainfrom
davismcphee:discover-group-by-fetching

Conversation

@davismcphee
Copy link
Copy Markdown
Contributor

@davismcphee davismcphee commented Jan 31, 2026

Summary

This PR implements support for group by fetching across Discover tabs, by detaching the data fetching logic from the React tree and consolidating it in a new CascadedDocumentsFetcher. This allows for the following behaviours:

  • The active fetch is cancelled when a group is expanded then collapsed while the fetch is running.
  • The active fetch is not cancelled when switching Discover tabs while the fetch is running, allowing it to run to completion in the background.
  • When returning to a Discover tab and re-expanding a previously fetched group, its records are no longer re-fetched and instead served from a cache.
  • All fetched groups are cleared when triggering a top-level Discover fetch, ensuring we don't show stale data in groups.
  • All active fetches are cancelled when leaving Discover or triggering a top-level Discover fetch, ensuring they aren't accidentally left running.

Group by UI state is not currently retained when switching Discover tabs, that will be handled in a followup.

Resolves #249455.

Checklist

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests were updated or added to match the most common scenarios
  • If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker list
  • This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The release_note:breaking label should be applied in these situations.
  • Flaky Test Runner was used on any tests changed
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines
  • Review the backport guidelines and apply applicable backport:* labels.

@davismcphee davismcphee self-assigned this Jan 31, 2026
@davismcphee davismcphee added release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Team:DataDiscovery Discover, search (data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. t// labels Jan 31, 2026
@davismcphee davismcphee force-pushed the discover-group-by-fetching branch 3 times, most recently from c499ba2 to 20a3d07 Compare February 5, 2026 04:06
@davismcphee davismcphee force-pushed the discover-group-by-fetching branch from 20a3d07 to c3e922f Compare February 10, 2026 23:41
@kibanamachine
Copy link
Copy Markdown
Contributor

Flaky Test Runner Stats

🎉 All tests passed! - kibana-flaky-test-suite-runner#10718

[✅] src/platform/test/functional/apps/discover/cascade_layout/config.ts: 25/25 tests passed.

see run history

Comment on lines +21 to +36
const EMPTY_FILTERS: Filter[] = [];

export const selectTabCombinedFilters = createSelector(
[
(tab: TabState) => tab.globalState.filters,
(tab: TabState) => tab.appState.filters,
(tab: TabState) => tab.appState.query,
],
(globalFilters, appFilters, query) => {
if (isOfAggregateQueryType(query)) {
return EMPTY_FILTERS;
}
const allFilters = [...(globalFilters ?? []), ...(appFilters ?? [])];
return allFilters.length ? cloneDeep(allFilters) : EMPTY_FILTERS;
}
);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I encountered an issue when switching Discover tabs where an additional fetch was triggered for the expanded group because we were relying on global services for filters, which are updated during tab change transition.

Technically it was fixed by stabilizing the handlers in useDataCascadeRowExpansionHandlers, but I had already made this change and figured it would be a good idea to standardize on a non-global service approach to selecting all filters to prevent similar issues in the future. I believe I updated all areas in Discover where this is done to use the new memoized selector.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for cleaning up the filters setup!

@davismcphee davismcphee marked this pull request as ready for review February 11, 2026 16:49
@davismcphee davismcphee requested a review from a team as a code owner February 11, 2026 16:49
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/kibana-data-discovery (Team:DataDiscovery)

dataView,
timeRange,
}: FetchCascadedDocumentsParams) {
this.cancelFetch(nodeId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

q1: this is because we can't have multiple cascades open at the same time so we cancel the current one?
q2: if we go back to a cascade that was already fetched this will just remove the abort controller from the map but nothing will really happen?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is because we can't have multiple cascades open at the same time so we cancel the current one?

Yes only one group at a time, but cancelling in-flight requests should already be handled via external cancelFetch calls. This is mainly just a safeguard against duplicate fetch attempts (shouldn't really happen though).

if we go back to a cascade that was already fetched this will just remove the abort controller from the map but nothing will really happen?

Exactly, it should be a no-op for already fetched groups. However, the abort controller is removed after the first fetch, so it would just check the map and return when it doesn't find one.

const abortController = this.abortControllers.get(nodeId);

if (abortController) {
abortController.abort();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

really small thing: i guess it comes from this part, right now when aborting we get an error in the console

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah unfortunately I'm not sure we can fix this on our end atm. I originally caught errors and returned an empty array, but it caused the cascade components to cache the empty results. cc @eokoneyo in case we want to consider ways to improve the "failed fetch" case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd take a look at this

Copy link
Copy Markdown
Contributor

@AlexGPlay AlexGPlay left a comment

Choose a reason for hiding this comment

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

manually tested + code lgtm but i'm not an expert on this so you may want a second review 🙈

not related to this task but it'd be nice to keep the cascades open between tabs navigations so when i come back from another tab the one i was taking a look at is still open

Copy link
Copy Markdown
Contributor

@jughosta jughosta left a comment

Choose a reason for hiding this comment

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

LGTM and works great 👍

Comment on lines +21 to +36
const EMPTY_FILTERS: Filter[] = [];

export const selectTabCombinedFilters = createSelector(
[
(tab: TabState) => tab.globalState.filters,
(tab: TabState) => tab.appState.filters,
(tab: TabState) => tab.appState.query,
],
(globalFilters, appFilters, query) => {
if (isOfAggregateQueryType(query)) {
return EMPTY_FILTERS;
}
const allFilters = [...(globalFilters ?? []), ...(appFilters ?? [])];
return allFilters.length ? cloneDeep(allFilters) : EMPTY_FILTERS;
}
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for cleaning up the filters setup!

await expectCascadeRequestTimestampToChange(baseline, false);
});
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could also extend by checking how many requests were triggered via discover.expectSearchRequestCount helper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For context, I went with the inspector approach here so we also have e2e coverage for the custom request adapter changes. I took a shot at making it work with expectSearchRequestCount too, but was running into flakiness due to editor requests when changing tabs. I'm gonna merge this now to get it in, but open to revisiting and exploring expectSearchRequestCount again after.

@elasticmachine
Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] Scout: [ platform / workflows_management ] plugin / local-stateful-classic - Workflow execution - Foreach iterations - should display scrollable step executions with many iterations

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
discover 2078 2080 +2

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
discover 1.6MB 1.6MB +341.0B

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
discover 25.1KB 25.1KB -13.0B
Unknown metric groups

async chunk count

id before after diff
discover 44 43 -1

History

cc @davismcphee

@davismcphee davismcphee merged commit a64911a into elastic:main Feb 18, 2026
16 checks passed
@davismcphee davismcphee deleted the discover-group-by-fetching branch February 18, 2026 01:42
patrykkopycinski pushed a commit to patrykkopycinski/kibana that referenced this pull request Feb 19, 2026
…251154)

## Summary

This PR implements support for group by fetching across Discover tabs,
by detaching the data fetching logic from the React tree and
consolidating it in a new `CascadedDocumentsFetcher`. This allows for
the following behaviours:
- The active fetch is cancelled when a group is expanded then collapsed
while the fetch is running.
- The active fetch is not cancelled when switching Discover tabs while
the fetch is running, allowing it to run to completion in the
background.
- When returning to a Discover tab and re-expanding a previously fetched
group, its records are no longer re-fetched and instead served from a
cache.
- All fetched groups are cleared when triggering a top-level Discover
fetch, ensuring we don't show stale data in groups.
- All active fetches are cancelled when leaving Discover or triggering a
top-level Discover fetch, ensuring they aren't accidentally left
running.

Group by UI state is not currently retained when switching Discover
tabs, that will be handled in a followup.

Resolves elastic#249455.

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [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
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [x] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [x] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.
ersin-erdal pushed a commit to ersin-erdal/kibana that referenced this pull request Feb 19, 2026
…251154)

## Summary

This PR implements support for group by fetching across Discover tabs,
by detaching the data fetching logic from the React tree and
consolidating it in a new `CascadedDocumentsFetcher`. This allows for
the following behaviours:
- The active fetch is cancelled when a group is expanded then collapsed
while the fetch is running.
- The active fetch is not cancelled when switching Discover tabs while
the fetch is running, allowing it to run to completion in the
background.
- When returning to a Discover tab and re-expanding a previously fetched
group, its records are no longer re-fetched and instead served from a
cache.
- All fetched groups are cleared when triggering a top-level Discover
fetch, ensuring we don't show stale data in groups.
- All active fetches are cancelled when leaving Discover or triggering a
top-level Discover fetch, ensuring they aren't accidentally left
running.

Group by UI state is not currently retained when switching Discover
tabs, that will be handled in a followup.

Resolves elastic#249455.

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [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
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [x] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [x] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes Team:DataDiscovery Discover, search (data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. t// v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Discover] [Group By] Lift data fetching out of React components to support fetching across tabs

6 participants