diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 18996a7cdf9ca..376b1f3e2476b 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -46,7 +46,6 @@ import useObservable from 'react-use/lib/useObservable'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { DiscoverGridSettings } from '@kbn/saved-search-plugin/common'; import { useQuerySubscriber } from '@kbn/unified-field-list'; -import { map } from 'rxjs'; import { DiscoverGrid } from '../../../../components/discover_grid'; import { getDefaultRowsPerPage } from '../../../../../common/constants'; import { useInternalStateSelector } from '../../state_management/discover_internal_state_container'; @@ -110,6 +109,7 @@ function DiscoverDocumentsComponent({ }) { const services = useDiscoverServices(); const documents$ = stateContainer.dataState.data$.documents$; + const main$ = stateContainer.dataState.data$.main$; const savedSearch = useSavedSearchInitial(); const { dataViews, capabilities, uiSettings, uiActions, ebtManager, fieldsMetadata } = services; const [ @@ -272,17 +272,12 @@ function DiscoverDocumentsComponent({ const { filters } = useQuerySubscriber({ data: services.data }); - const timeRange = useObservable( - services.timefilter.getTimeUpdate$().pipe(map(() => services.timefilter.getTime())), - services.timefilter.getTime() - ); - const cellActionsMetadata = useAdditionalCellActions({ dataSource, dataView, query, filters, - timeRange, + timeRange: main$.getValue().params?.timeRange, }); const renderDocumentView = useCallback( diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx index 9aec3589c753e..0d3ac14e93bdc 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx @@ -65,12 +65,6 @@ const mountComponent = async ({ const dataView = savedSearch?.searchSource?.getField('index') as DataView; let services = discoverServiceMock; - services.data.query.timefilter.timefilter.getAbsoluteTime = () => { - return { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }; - }; - services.data.query.timefilter.timefilter.getTime = () => { - return { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }; - }; (services.data.query.queryString.getDefaultQuery as jest.Mock).mockReturnValue({ language: 'kuery', query: '', @@ -86,6 +80,11 @@ const mountComponent = async ({ const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, foundDocuments: true, + params: { + dataView, + timeRange: { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }, + timeRangeRelative: { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }, + }, }) as DataMain$; const documents$ = new BehaviorSubject({ diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx index 13a8e2c9c402a..8499aa6e77d37 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx @@ -53,21 +53,21 @@ async function mountComponent( main$: DataMain$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, foundDocuments: true, + params: { + dataView, + timeRange: { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }, + timeRangeRelative: { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }, + }, }) as DataMain$ ) { const searchSourceMock = createSearchSourceMock({ index: dataView }); const services = createDiscoverServicesMock(); - const time = { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' }; - services.data.query.timefilter.timefilter.getTime = () => time; + (services.data.query.queryString.getDefaultQuery as jest.Mock).mockReturnValue({ language: 'kuery', query: '', }); - (services.data.query.getState as jest.Mock).mockReturnValue({ - filters: [], - query, - time, - }); + (searchSourceInstanceMock.fetch$ as jest.Mock).mockImplementation( jest.fn().mockReturnValue(of({ rawResponse: { hits: { total: 2 } } })) ); diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index 3f2acf0ce933b..bb992f3eee4f0 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts @@ -7,7 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; import { canImportVisContext, UnifiedHistogramApi, @@ -23,6 +22,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { debounceTime, distinctUntilChanged, + distinctUntilKeyChanged, filter, map, merge, @@ -42,12 +42,11 @@ import type { InspectorAdapters } from '../../hooks/use_inspector'; import { checkHitCount, sendErrorTo } from '../../hooks/use_saved_search_messages'; import type { DiscoverStateContainer } from '../../state_management/discover_state'; import { addLog } from '../../../../utils/add_log'; -import { useInternalStateSelector } from '../../state_management/discover_internal_state_container'; import { useAppStateSelector, type DiscoverAppState, } from '../../state_management/discover_app_state_container'; -import { DataDocumentsMsg } from '../../state_management/discover_data_state_container'; +import { DataDocumentsMsg, DataMain$ } from '../../state_management/discover_data_state_container'; import { useSavedSearch } from '../../state_management/discover_state_provider'; import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; @@ -133,22 +132,20 @@ export const useDiscoverHistogram = ({ */ useEffect(() => { - const subscription = createAppStateObservable(stateContainer.appState.state$).subscribe( - (changes) => { - if ('timeInterval' in changes && changes.timeInterval) { - unifiedHistogram?.setTimeInterval(changes.timeInterval); - } + const subscription = createAppStateObservable(main$).subscribe((changes) => { + if ('timeInterval' in changes && changes.timeInterval) { + unifiedHistogram?.setTimeInterval(changes.timeInterval); + } - if ('chartHidden' in changes && typeof changes.chartHidden === 'boolean') { - unifiedHistogram?.setChartHidden(changes.chartHidden); - } + if ('chartHidden' in changes && typeof changes.chartHidden === 'boolean') { + unifiedHistogram?.setChartHidden(changes.chartHidden); } - ); + }); return () => { subscription?.unsubscribe(); }; - }, [stateContainer.appState.state$, unifiedHistogram]); + }, [unifiedHistogram, main$]); /** * Total hits @@ -216,14 +213,14 @@ export const useDiscoverHistogram = ({ /** * Request params */ - const { query, filters } = useQuerySubscriber({ data: services.data }); - const customFilters = useInternalStateSelector((state) => state.customFilters); - const timefilter = services.data.query.timefilter.timefilter; - const timeRange = timefilter.getAbsoluteTime(); - const relativeTimeRange = useObservable( - timefilter.getTimeUpdate$().pipe(map(() => timefilter.getTime())), - timefilter.getTime() + const { params } = useObservable( + main$.pipe( + filter(({ fetchStatus }) => fetchStatus === FetchStatus.LOADING), + distinctUntilKeyChanged('fetchTime') + ), + main$.getValue() ); + const { timeRangeRelative, timeRange, customFilters, query, filters, dataView } = params || {}; // When in ES|QL mode, update the data view, query, and // columns only when documents are done fetching so the Lens suggestions @@ -328,18 +325,13 @@ export const useDiscoverHistogram = ({ }; }, [isEsqlMode, stateContainer.dataState.fetchChart$, esqlFetchComplete$, unifiedHistogram]); - const dataView = useInternalStateSelector((state) => state.dataView!); - const histogramCustomization = useDiscoverCustomization('unified_histogram'); const filtersMemoized = useMemo(() => { - const allFilters = [...(filters ?? []), ...customFilters]; + const allFilters = [...(filters ?? []), ...(customFilters ?? [])]; return allFilters.length ? allFilters : EMPTY_FILTERS; }, [filters, customFilters]); - // eslint-disable-next-line react-hooks/exhaustive-deps - const timeRangeMemoized = useMemo(() => timeRange, [timeRange?.from, timeRange?.to]); - const onVisContextChanged = useCallback( ( nextVisContext: UnifiedHistogramVisContext | undefined, @@ -396,11 +388,11 @@ export const useDiscoverHistogram = ({ ref, getCreationOptions, services, - dataView: isEsqlMode ? esqlDataView : dataView, + dataView: isEsqlMode ? esqlDataView : dataView!, query: isEsqlMode ? esqlQuery : query, filters: filtersMemoized, - timeRange: timeRangeMemoized, - relativeTimeRange, + timeRange, + relativeTimeRange: timeRangeRelative, columns: isEsqlMode ? esqlColumns : undefined, onFilter: histogramCustomization?.onFilter, onBrushEnd: histogramCustomization?.onBrushEnd, @@ -452,7 +444,7 @@ const createUnifiedHistogramStateObservable = (state$?: Observable) => { +const createAppStateObservable = (state$: DataMain$) => { return state$.pipe( startWith(undefined), pairwise(), @@ -463,12 +455,12 @@ const createAppStateObservable = (state$: Observable) => { return changes; } - if (prev?.interval !== curr.interval) { - changes.timeInterval = curr.interval; + if (prev?.params?.timeInterval !== curr.params?.timeInterval) { + changes.timeInterval = curr.params?.timeInterval; } - if (prev?.hideChart !== curr.hideChart) { - changes.chartHidden = curr.hideChart; + if (prev?.params?.hideChart !== curr.params?.hideChart) { + changes.chartHidden = curr.params?.hideChart; } return changes; diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts index f8552411c0add..42abf25ee2ddc 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts @@ -97,6 +97,7 @@ export function fetchAll( services, sort: getAppState().sort as SortOrder[], customFilters: getInternalState().customFilters, + inputTimeRange: dataSubjects.main$.getValue().params?.timeRange, }); } @@ -117,6 +118,7 @@ export function fetchAll( data, expressions, profilesManager, + inputTimeRange: dataSubjects.main$.getValue().params?.timeRange, }) : fetchDocuments(searchSource, fetchDeps); const fetchType = isEsqlQuery ? 'fetchTextBased' : 'fetchDocuments'; diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts index dca01247296a4..ad681bd969fe4 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts @@ -51,7 +51,7 @@ export function fetchEsql({ expressions: ExpressionsStart; profilesManager: ProfilesManager; }): Promise { - const timeRange = inputTimeRange ?? data.query.timefilter.timefilter.getTime(); + const timeRange = inputTimeRange ?? data.query.timefilter.timefilter.getAbsoluteTime(); return textBasedQueryStateToAstWithValidation({ filters, query, diff --git a/src/plugins/discover/public/application/main/data_fetching/update_search_source.ts b/src/plugins/discover/public/application/main/data_fetching/update_search_source.ts index ad79e93ec37e4..115c8c782ec89 100644 --- a/src/plugins/discover/public/application/main/data_fetching/update_search_source.ts +++ b/src/plugins/discover/public/application/main/data_fetching/update_search_source.ts @@ -9,7 +9,7 @@ import { ISearchSource } from '@kbn/data-plugin/public'; import { DataViewType, DataView } from '@kbn/data-views-plugin/public'; -import { Filter } from '@kbn/es-query'; +import { Filter, TimeRange } from '@kbn/es-query'; import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { SORT_DEFAULT_ORDER_SETTING } from '@kbn/discover-utils'; import { DiscoverServices } from '../../../build_services'; @@ -25,11 +25,13 @@ export function updateVolatileSearchSource( services, sort, customFilters, + inputTimeRange, }: { dataView: DataView; services: DiscoverServices; sort?: SortOrder[]; customFilters: Filter[]; + inputTimeRange?: TimeRange; } ) { const { uiSettings, data } = services; @@ -48,7 +50,7 @@ export function updateVolatileSearchSource( if (dataView.type !== DataViewType.ROLLUP) { // Set the date range filter fields from timeFilter using the absolute format. Search sessions requires that it be converted from a relative range - const timeFilter = data.query.timefilter.timefilter.createFilter(dataView); + const timeFilter = data.query.timefilter.timefilter.createFilter(dataView, inputTimeRange); filters = timeFilter ? [...filters, timeFilter] : filters; } diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts index dbe7da63f5e76..c840d4e14bd5e 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts @@ -11,9 +11,10 @@ import type { BehaviorSubject } from 'rxjs'; import type { DataTableRecord } from '@kbn/discover-utils/src/types'; import type { SearchResponseWarning } from '@kbn/search-response-warnings'; import { FetchStatus } from '../../types'; -import type { +import { DataDocuments$, DataMain$, + DataMainMsgParams, DataMsg, DataTotalHits$, SavedSearchData, @@ -37,7 +38,24 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { if (main$.getValue().fetchStatus === FetchStatus.COMPLETE) { return; } - main$.next({ fetchStatus: FetchStatus.COMPLETE, foundDocuments, error: undefined }); + main$.next({ + ...main$.getValue(), + fetchStatus: FetchStatus.COMPLETE, + foundDocuments, + error: undefined, + }); +} + +/** + * Send message when data fetching starts via main observable + */ +export function sendFetchStartMsg(main$: DataMain$, params: DataMainMsgParams) { + main$.next({ + ...main$.getValue(), + params, + fetchStatus: FetchStatus.LOADING, + fetchTime: new Date().toISOString(), + }); } /** @@ -45,7 +63,7 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { */ export function sendPartialMsg(main$: DataMain$) { if (main$.getValue().fetchStatus === FetchStatus.LOADING) { - main$.next({ fetchStatus: FetchStatus.PARTIAL }); + main$.next({ ...main$.getValue(), fetchStatus: FetchStatus.PARTIAL }); } } @@ -57,7 +75,7 @@ export function sendLoadingMsg( props?: Omit ) { if (data$.getValue().fetchStatus !== FetchStatus.LOADING) { - data$.next({ ...props, fetchStatus: FetchStatus.LOADING } as T); + data$.next({ ...data$.getValue(), ...props, fetchStatus: FetchStatus.LOADING } as T); } } @@ -108,7 +126,14 @@ export function sendErrorMsg(data$: DataMain$ | DataDocuments$ | DataTotalHits$, * Needed when data view is switched or a new runtime field is added */ export function sendResetMsg(data: SavedSearchData, initialFetchStatus: FetchStatus) { - data.main$.next({ fetchStatus: initialFetchStatus, foundDocuments: undefined }); + const dataView = data.main$.getValue().params?.dataView; + data.main$.next({ + fetchStatus: initialFetchStatus, + foundDocuments: undefined, + params: { + dataView, + }, + }); data.documents$.next({ fetchStatus: initialFetchStatus, result: [] }); data.totalHits$.next({ fetchStatus: initialFetchStatus, result: undefined }); } diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts index 59220d7def3c1..d243f7bc9c990 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts @@ -12,7 +12,7 @@ import type { AutoRefreshDoneFn } from '@kbn/data-plugin/public'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query'; +import { AggregateQuery, Filter, isOfAggregateQueryType, Query, TimeRange } from '@kbn/es-query'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import type { DataView } from '@kbn/data-views-plugin/common'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; @@ -26,7 +26,7 @@ import type { DiscoverSearchSessionManager } from './discover_search_session'; import { FetchStatus } from '../../types'; import { validateTimeRange } from './utils/validate_time_range'; import { fetchAll, fetchMoreDocuments } from '../data_fetching/fetch_all'; -import { sendResetMsg } from '../hooks/use_saved_search_messages'; +import { sendResetMsg, sendFetchStartMsg } from '../hooks/use_saved_search_messages'; import { getFetch$ } from '../data_fetching/get_fetch_observable'; import type { DiscoverInternalStateContainer } from './discover_internal_state_container'; import { getDefaultProfileState } from './utils/get_default_profile_state'; @@ -53,6 +53,19 @@ export interface DataMsg { export interface DataMainMsg extends DataMsg { foundDocuments?: boolean; + params?: DataMainMsgParams; + fetchTime?: string; +} + +export interface DataMainMsgParams { + customFilters?: Filter[]; + dataView?: DataView; + filters?: Filter[]; + hideChart?: boolean; + timeRange?: TimeRange; + timeRangeRelative?: TimeRange; + timeInterval?: string; + query?: AggregateQuery | Query | undefined; } export interface DataDocumentsMsg extends DataMsg { @@ -235,6 +248,16 @@ export function getDataStateContainer({ abortController?.abort(); abortControllerFetchMore?.abort(); + sendFetchStartMsg(dataSubjects.main$, { + customFilters: internalStateContainer.getState().customFilters, + dataView: internalStateContainer.getState().dataView, + filters: appStateContainer.getState().filters, + hideChart: appStateContainer.getState().hideChart, + query: appStateContainer.getState().query, + timeRange: timefilter.getAbsoluteTime(), + timeRangeRelative: timefilter.getTime(), + timeInterval: appStateContainer.getState().interval, + }); if (options.fetchMore) { abortControllerFetchMore = new AbortController();