diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index ccda27870313..26daee2c58bf 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -234,6 +234,14 @@ export class IndexPatternsService { indexPatternCache.set(id, indexPattern); }; + isPresentInCache(id: string) { + const indexPattern = indexPatternCache.get(id); + if (indexPattern) { + return true; + } + return false; + } + /** * Get default index pattern */ diff --git a/src/plugins/data/common/search/search_source/create_search_source.ts b/src/plugins/data/common/search/search_source/create_search_source.ts index 74b164369018..ac9fc5a9cbe6 100644 --- a/src/plugins/data/common/search/search_source/create_search_source.ts +++ b/src/plugins/data/common/search/search_source/create_search_source.ts @@ -28,6 +28,7 @@ * under the License. */ +import { QueryStringContract } from 'src/plugins/data/public/'; import { migrateLegacyQuery } from './migrate_legacy_query'; import { SearchSource, SearchSourceDependencies } from './search_source'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; @@ -52,10 +53,20 @@ import { SearchSourceFields } from './types'; * @public */ export const createSearchSource = ( indexPatterns: IndexPatternsContract, + queryStringService: QueryStringContract, searchSourceDependencies: SearchSourceDependencies ) => async (searchSourceFields: SearchSourceFields = {}) => { const fields = { ...searchSourceFields }; + // When we load a saved search and the saved search contains a non index pattern data source this step creates the temperary index patterns and sets the appriopriate query + if ( + fields.query?.dataset && + fields.query?.dataset?.type !== 'INDEX_PATTERN' && + !indexPatterns.isPresentInCache(fields.query.dataset.id) + ) { + await queryStringService.getDatasetService().cacheDataset(fields.query?.dataset); + } + // hydrating index pattern if (fields.index && typeof fields.index === 'string') { fields.index = await indexPatterns.get(searchSourceFields.index as any); diff --git a/src/plugins/data/common/search/search_source/search_source_service.test.ts b/src/plugins/data/common/search/search_source/search_source_service.test.ts index 8be71dbdacc2..da24c295c563 100644 --- a/src/plugins/data/common/search/search_source/search_source_service.test.ts +++ b/src/plugins/data/common/search/search_source/search_source_service.test.ts @@ -31,6 +31,7 @@ import { BehaviorSubject } from 'rxjs'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; import { SearchSourceService, SearchSourceDependencies } from './'; +import { QueryStringContract } from 'src/plugins/data/public'; describe('SearchSource service', () => { let dependencies: jest.Mocked; @@ -52,6 +53,7 @@ describe('SearchSource service', () => { test('exposes proper contract', () => { const start = new SearchSourceService().start( (jest.fn() as unknown) as jest.Mocked, + (jest.fn() as unknown) as jest.Mocked, dependencies ); diff --git a/src/plugins/data/common/search/search_source/search_source_service.ts b/src/plugins/data/common/search/search_source/search_source_service.ts index b16b755d621d..b20602b3b6c6 100644 --- a/src/plugins/data/common/search/search_source/search_source_service.ts +++ b/src/plugins/data/common/search/search_source/search_source_service.ts @@ -28,18 +28,23 @@ * under the License. */ +import { QueryStringContract } from 'src/plugins/data/public'; import { createSearchSource, SearchSource, SearchSourceDependencies } from './'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; export class SearchSourceService { public setup() {} - public start(indexPatterns: IndexPatternsContract, dependencies: SearchSourceDependencies) { + public start( + indexPatterns: IndexPatternsContract, + queryStringService: QueryStringContract, + dependencies: SearchSourceDependencies + ) { return { /** * creates searchsource based on serialized search source fields */ - create: createSearchSource(indexPatterns, dependencies), + create: createSearchSource(indexPatterns, queryStringService, dependencies), /** * creates an enpty search source */ diff --git a/src/plugins/data/public/query/query_string/query_string_manager.ts b/src/plugins/data/public/query/query_string/query_string_manager.ts index 23aa938ba23a..bce9dd45956b 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.ts @@ -104,7 +104,7 @@ export class QueryStringManager { } public getUpdates$ = () => { - return this.query$.asObservable().pipe(skip(1)); + return this.query$.asObservable().pipe(); }; public getQuery = (): Query => { diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 57881b08957f..c65dee873a7d 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -209,13 +209,19 @@ export class SearchService implements Plugin { df: dfService, }; + const queryString = getQueryService().queryString; + return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), search, showError: (e: Error) => { this.searchInterceptor.showError(e); }, - searchSource: this.searchSourceService.start(indexPatterns, searchSourceDependencies), + searchSource: this.searchSourceService.start( + indexPatterns, + queryString, + searchSourceDependencies + ), __enhance: (enhancements: SearchEnhancements) => { this.searchInterceptor = enhancements.searchInterceptor; }, diff --git a/src/plugins/data/public/ui/dataset_selector/index.tsx b/src/plugins/data/public/ui/dataset_selector/index.tsx index fa6dea8f0035..95a083a5ad8a 100644 --- a/src/plugins/data/public/ui/dataset_selector/index.tsx +++ b/src/plugins/data/public/ui/dataset_selector/index.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import React from 'react'; import { Dataset, Query, TimeRange } from '../../../common'; import { DatasetSelector } from './dataset_selector'; @@ -18,9 +18,21 @@ const ConnectedDatasetSelector = ({ onSubmit }: ConnectedDatasetSelectorProps) = const { services } = useOpenSearchDashboards(); const queryString = services.data.query.queryString; const [selectedDataset, setSelectedDataset] = useState( - () => queryString.getQuery().dataset || queryString.getDefaultQuery().dataset + () => queryString.getQuery().dataset ); + useEffect(() => { + const subscription = queryString.getUpdates$().subscribe((query: Query) => { + if (query.dataset !== selectedDataset) { + setSelectedDataset(query.dataset); + } + }); + return () => { + subscription.unsubscribe(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [queryString]); + const handleDatasetChange = useCallback( (dataset?: Dataset) => { setSelectedDataset(dataset); diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index c2d79fb6d470..9f0092f1158c 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -241,11 +241,10 @@ export default class QueryEditorUI extends Component { if (!isEqual(this.props.query.dataset, prevQuery.dataset)) { if (this.inputRef) { - const newQuery = this.queryString.getInitialQuery(); - const newQueryString = newQuery.query; - if (this.inputRef.getValue() !== newQueryString) { + const newQueryString = this.props.query.query; + if (this.inputRef.getValue() !== newQueryString && typeof newQueryString === 'string') { this.inputRef.setValue(newQueryString); - this.onSubmit(newQuery); + this.onSubmit(this.props.query); } } } diff --git a/src/plugins/data_explorer/public/utils/state_management/store.ts b/src/plugins/data_explorer/public/utils/state_management/store.ts index daf0b3d7e369..daded4a6f3cc 100644 --- a/src/plugins/data_explorer/public/utils/state_management/store.ts +++ b/src/plugins/data_explorer/public/utils/state_management/store.ts @@ -87,7 +87,11 @@ export const getPreloadedStore = async (services: DataExplorerServices) => { // If the url state is different from the current state, then we need to update the store // the state should have a view property if it was loaded from the url - if (action === 'POP' && urlState.metadata?.view && !isEqual(urlState, currentState)) { + if ( + (action === 'POP' || action === 'REPLACE') && + urlState.metadata?.view && + !isEqual(urlState, currentState) + ) { store.dispatch(hydrate(urlState as RootState)); } }); diff --git a/src/plugins/discover/public/application/utils/state_management/discover_slice.tsx b/src/plugins/discover/public/application/utils/state_management/discover_slice.tsx index 026599d7fc65..8bd0a8e0ea2f 100644 --- a/src/plugins/discover/public/application/utils/state_management/discover_slice.tsx +++ b/src/plugins/discover/public/application/utils/state_management/discover_slice.tsx @@ -11,7 +11,11 @@ import { RootState, DefaultViewState } from '../../../../../data_explorer/public import { buildColumns } from '../columns'; import * as utils from './common'; import { SortOrder } from '../../../saved_searches/types'; -import { DEFAULT_COLUMNS_SETTING, PLUGIN_ID } from '../../../../common'; +import { + DEFAULT_COLUMNS_SETTING, + PLUGIN_ID, + QUERY_ENHANCEMENT_ENABLED_SETTING, +} from '../../../../common'; export interface DiscoverState { /** @@ -88,12 +92,22 @@ export const getPreloadedState = async ({ preloadedState.state.sort = savedSearchInstance.sort; preloadedState.state.savedSearch = savedSearchInstance.id; const indexPatternId = savedSearchInstance.searchSource.getField('index')?.id; - preloadedState.root = { - metadata: { - indexPattern: indexPatternId, - view: PLUGIN_ID, - }, - }; + + // If the query enhancement is enabled don't add the indexpattern id to the root since they will be migrated into the _q state + if (config.get(QUERY_ENHANCEMENT_ENABLED_SETTING)) { + preloadedState.root = { + metadata: { + view: PLUGIN_ID, + }, + }; + } else { + preloadedState.root = { + metadata: { + indexPattern: indexPatternId, + view: PLUGIN_ID, + }, + }; + } savedSearchInstance.destroy(); // this instance is no longer needed, will create another one later } diff --git a/src/plugins/discover/public/application/view_components/utils/use_search.ts b/src/plugins/discover/public/application/view_components/utils/use_search.ts index 34264540c5ec..9912a44c6006 100644 --- a/src/plugins/discover/public/application/view_components/utils/use_search.ts +++ b/src/plugins/discover/public/application/view_components/utils/use_search.ts @@ -331,13 +331,14 @@ export const useSearch = (services: DiscoverViewServices) => { useEffect(() => { (async () => { const savedSearchInstance = await getSavedSearchById(savedSearchId); - setSavedSearch(savedSearchInstance); // if saved search does not exist, do not atempt to sync filters and query from savedObject - if (!savedSearch) { + if (!savedSearchInstance) { return; } + setSavedSearch(savedSearchInstance); + // sync initial app filters from savedObject to filterManager const filters = cloneDeep(savedSearchInstance.searchSource.getOwnField('filter')); const query = diff --git a/src/plugins/query_enhancements/public/plugin.tsx b/src/plugins/query_enhancements/public/plugin.tsx index a3cec8802880..b790dd7f70ca 100644 --- a/src/plugins/query_enhancements/public/plugin.tsx +++ b/src/plugins/query_enhancements/public/plugin.tsx @@ -90,7 +90,7 @@ export class QueryEnhancementsPlugin title: 'SQL', search: sqlSearchInterceptor, getQueryString: (query: Query) => { - return `SELECT * FROM ${queryString.getQuery().dataset?.title} LIMIT 10`; + return `SELECT * FROM ${query.dataset?.title} LIMIT 10`; }, fields: { filterable: false,