From d36d38ad573df4509cebca44f855414f579bd045 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 00:33:37 +0000 Subject: [PATCH] [Discover] Skip loading filter if navigating to a saved search without params (#10913) * [Discover] Skip loading filter if navigating to a saved search without params Signed-off-by: Joey Liu * Changeset file for PR #10913 created/updated * Always skip URL cache in discover if _q is absent Signed-off-by: Joey Liu --------- Signed-off-by: Joey Liu Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> (cherry picked from commit a47de8828fb199e93894a2a4fdf119be2856df0f) Signed-off-by: github-actions[bot] --- changelogs/fragments/10913.yml | 2 + .../state_sync/connect_to_query_state.test.ts | 102 ++++++++++++++++++ .../state_sync/connect_to_query_state.ts | 31 ++++-- .../view_components/canvas/top_nav.tsx | 1 + 4 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 changelogs/fragments/10913.yml diff --git a/changelogs/fragments/10913.yml b/changelogs/fragments/10913.yml new file mode 100644 index 000000000000..0186b42f8d60 --- /dev/null +++ b/changelogs/fragments/10913.yml @@ -0,0 +1,2 @@ +fix: +- Skip loading filter if navigating to a saved search without params ([#10913](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/10913)) \ No newline at end of file diff --git a/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts b/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts index 85dad6982f01..f4fa27c548d3 100644 --- a/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts +++ b/src/plugins/data/public/query/state_sync/connect_to_query_state.test.ts @@ -215,6 +215,108 @@ describe('connect_storage_to_query_state', () => { const updatedStorage = osdUrlStateStorage.get('_q'); expect(previousStorage).not.toStrictEqual(updatedStorage); }); + + test('when skipAppFiltersFromMemory is true, state initializes with empty filters even if filterManager has app filters', () => { + // Set some app filters in filterManager + filterManager.setFilters([aF1, aF2]); + expect(filterManager.getAppFilters().length).toBe(2); + + expect(osdUrlStateStorage.get('_q')).toBeNull(); + + // Connect with skipAppFiltersFromMemory enabled + connectStorageToQueryState(queryServiceStart, osdUrlStateStorage, { + filters: FilterStateStore.APP_STATE, + query: true, + skipAppFiltersFromMemory: true, + }); + + // State should have empty filters, not filters from filterManager + expect(osdUrlStateStorage.get('_q')).toEqual({ + query: queryString.getDefaultQuery(), + filters: [], + }); + }); + + test('when skipAppFiltersFromMemory is true, app filters are cleared from filterManager', () => { + // Set some app filters in filterManager + filterManager.setFilters([aF1, aF2]); + expect(filterManager.getAppFilters().length).toBe(2); + + // Connect with skipAppFiltersFromMemory enabled + connectStorageToQueryState(queryServiceStart, osdUrlStateStorage, { + filters: FilterStateStore.APP_STATE, + query: true, + skipAppFiltersFromMemory: true, + }); + + // App filters should be cleared + expect(filterManager.getAppFilters()).toEqual([]); + }); + + test('when skipAppFiltersFromMemory is false, state initializes with filters from filterManager', () => { + // Set some app filters in filterManager + filterManager.setFilters([aF1, aF2]); + + expect(osdUrlStateStorage.get('_q')).toBeNull(); + + // Connect with skipAppFiltersFromMemory disabled (default behavior) + connectStorageToQueryState(queryServiceStart, osdUrlStateStorage, { + filters: FilterStateStore.APP_STATE, + query: true, + skipAppFiltersFromMemory: false, + }); + + // State should have filters from filterManager + expect(osdUrlStateStorage.get('_q')).toEqual({ + query: queryString.getDefaultQuery(), + filters: [aF1, aF2], + }); + }); + + test('when skipAppFiltersFromMemory is true but URL has filters, URL filters take precedence', () => { + // Set some app filters in filterManager + filterManager.setFilters([aF1, aF2]); + + // Set different filters in URL + const urlFilters = [aF1]; + osdUrlStateStorage.set( + '_q', + { + filters: urlFilters, + query: q1, + }, + { + replace: true, + } + ); + + // Connect with skipAppFiltersFromMemory enabled + connectStorageToQueryState(queryServiceStart, osdUrlStateStorage, { + filters: FilterStateStore.APP_STATE, + query: true, + skipAppFiltersFromMemory: true, + }); + + // URL filters should be used, not empty array or filterManager filters + expect(filterManager.getFilters().length).toBe(1); + expect(queryString.getQuery()).toStrictEqual(q1); + }); + + test('when skipAppFiltersFromMemory is true with GLOBAL_STATE filters, app filters should not be cleared', () => { + // Set both global and app filters + filterManager.setFilters([gF1, aF1, aF2]); + expect(filterManager.getAppFilters().length).toBe(2); + + // Connect with skipAppFiltersFromMemory enabled but for GLOBAL_STATE + connectStorageToQueryState(queryServiceStart, osdUrlStateStorage, { + filters: FilterStateStore.GLOBAL_STATE, + query: true, + skipAppFiltersFromMemory: true, + }); + + // App filters should NOT be cleared because we're syncing GLOBAL_STATE + expect(filterManager.getAppFilters().length).toBe(2); + }); }); describe('connect_to_global_state', () => { diff --git a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts index 717361d2138d..b141e3aed075 100644 --- a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts +++ b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts @@ -44,6 +44,11 @@ export interface ISyncConfig { filters: FilterStateStore; query: boolean; dataset?: boolean; + /** + * When true, skips using existing filters from filterManager when initializing state from URL. + * This is useful when navigating to a saved search/explore to prevent filter persistence. + */ + skipAppFiltersFromMemory?: boolean; } /** @@ -72,36 +77,42 @@ export const connectStorageToQueryState = ( syncKeys.push('appFilters'); } - const initialStateFromURL: QueryState = osdUrlStateStorage.get('_q') ?? { + const initialState: QueryState = osdUrlStateStorage.get('_q') ?? { query: queryString.getDefaultQuery(), - filters: filterManager.getAppFilters(), + // If caller specifies to skip filters from memory, use empty array + filters: syncConfig.skipAppFiltersFromMemory ? [] : filterManager.getAppFilters(), }; if (!osdUrlStateStorage.get('_q')) { // set up initial '_q' flag in the URL to sync query and filter changes - osdUrlStateStorage.set('_q', initialStateFromURL, { + osdUrlStateStorage.set('_q', initialState, { replace: true, }); // clear existing query and apply default query queryString.clearQuery(); } - if (syncConfig.query && !_.isEqual(initialStateFromURL.query, queryString.getQuery())) { - if (initialStateFromURL.query) { - queryString.setQuery(_.cloneDeep(initialStateFromURL.query)); + // Clear app filters if caller requested to skip filters from memory + if (syncConfig.skipAppFiltersFromMemory && syncConfig.filters === FilterStateStore.APP_STATE) { + filterManager.setAppFilters([]); + } + + if (syncConfig.query && !_.isEqual(initialState.query, queryString.getQuery())) { + if (initialState.query) { + queryString.setQuery(_.cloneDeep(initialState.query)); } } if (syncConfig.filters === FilterStateStore.APP_STATE) { if ( - !initialStateFromURL.filters || - !compareFilters(initialStateFromURL.filters, filterManager.getAppFilters(), { + !initialState.filters || + !compareFilters(initialState.filters, filterManager.getAppFilters(), { ...COMPARE_ALL_OPTIONS, state: false, }) ) { - if (initialStateFromURL.filters) { - filterManager.setAppFilters(_.cloneDeep(initialStateFromURL.filters)); + if (initialState.filters) { + filterManager.setAppFilters(_.cloneDeep(initialState.filters)); } } diff --git a/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx b/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx index 7c8192881a98..e6a72b521d48 100644 --- a/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx +++ b/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx @@ -80,6 +80,7 @@ export const TopNav = ({ opts, showSaveQuery, isEnhancementsEnabled }: TopNavPro return { filters: opensearchFilters.FilterStateStore.APP_STATE, query: true, + skipAppFiltersFromMemory: true, }; }, []);