diff --git a/public/components/common/query_utils/index.ts b/public/components/common/query_utils/index.ts index eccd6b9ddd..0e3ad5bfff 100644 --- a/public/components/common/query_utils/index.ts +++ b/public/components/common/query_utils/index.ts @@ -347,6 +347,23 @@ export const composeFinalQuery = ( }); }; +export const composeFinalQueryWithoutTimestamp = ( + curQuery: string, + appBaseQuery: string, + selectedPatternField?: string, + patternRegex?: string, + filteredPattern?: string +) => { + let fullQuery = curQuery.includes(appBaseQuery) ? curQuery : buildQuery(appBaseQuery, curQuery); + if (isEmpty(fullQuery)) return ''; + + // if a pattern is selected as filter, build it into finalQuery + if (selectedPatternField && filteredPattern) + fullQuery = buildPatternsQuery(fullQuery, selectedPatternField, patternRegex, filteredPattern); + + return fullQuery; +}; + export const removeBacktick = (stringContainsBacktick: string) => { if (!stringContainsBacktick) return ''; return stringContainsBacktick.replace(/`/g, ''); diff --git a/public/components/common/search/date_picker.tsx b/public/components/common/search/date_picker.tsx index 8ca726f56b..af9154550f 100644 --- a/public/components/common/search/date_picker.tsx +++ b/public/components/common/search/date_picker.tsx @@ -5,6 +5,7 @@ import { EuiSuperDatePicker, EuiToolTip } from '@elastic/eui'; import React from 'react'; +import { i18n } from '@osd/i18n'; import { uiSettingsService } from '../../../../common/utils'; import { coreRefs } from '../../../framework/core_refs'; import { IDatePickerProps } from './search'; @@ -23,28 +24,40 @@ export function DatePicker(props: IDatePickerProps) { } = props; const handleTimeChange = (e: any) => handleTimePickerChange([e.start, e.end]); - const allowTimeChanging = !coreRefs.queryAssistEnabled || isAppAnalytics; + + let finalizedStartTime; + let finalizedEndTime; + let setDisabled; + let toolTipMessage; + + if (coreRefs.queryAssistEnabled && !isAppAnalytics) { + // is query assistant inside log explorer + finalizedStartTime = QUERY_ASSIST_START_TIME; + finalizedEndTime = QUERY_ASSIST_END_TIME; + setDisabled = true; + toolTipMessage = i18n.translate('discover.queryAssistant.timePickerDisabledMessage', { + defaultMessage: 'Date range has been disabled to accomodate timerange of all datasets', + }); + } else { + finalizedStartTime = startTime; + finalizedEndTime = endTime; + setDisabled = false; + toolTipMessage = false; + } return ( <> - + diff --git a/public/components/common/search/search.tsx b/public/components/common/search/search.tsx index 295909f282..1fff9b1c28 100644 --- a/public/components/common/search/search.tsx +++ b/public/components/common/search/search.tsx @@ -45,7 +45,7 @@ import { resetSummary, selectQueryAssistantSummarization, } from '../../event_analytics/redux/slices/query_assistant_summarization_slice'; -import { reset } from '../../event_analytics/redux/slices/query_result_slice'; +import { reset, selectQueryResult } from '../../event_analytics/redux/slices/query_result_slice'; import { changeData, changeQuery, @@ -123,6 +123,7 @@ export const Search = (props: any) => { } = props; const queryRedux = useSelector(selectQueries)[tabId]; + const queryResults = useSelector(selectQueryResult)[tabId]; const queryAssistantSummarization = useSelector(selectQueryAssistantSummarization)[tabId]; const dispatch = useDispatch(); const appLogEvents = tabId.match(APP_ANALYTICS_TAB_ID_REGEX); @@ -409,33 +410,35 @@ export const Search = (props: any) => { )} - - {!isLiveTailOn && ( - { - // modifies run button to look like the update button, if there is a time change, disables timepicker setting update if timepicker is disabled - setNeedsUpdate( - !showQueryArea && // keeps statement false if using query assistant ui, timepicker shouldn't change run button - !(tRange[0] === startTime && tRange[1] === endTime) // checks to see if the time given is different from prev - ); - // keeps the time range change local, to be used when update pressed - setStartTime(tRange[0]); - setEndTime(tRange[1]); - }} - handleTimeRangePickerRefresh={() => { - onQuerySearch(queryLang); - }} - isAppAnalytics={isAppAnalytics} - /> - )} - + {!(queryRedux.selectedTimestamp === '' && queryResults?.datarows) && ( // index with no timestamp, dont show timepicker + + {!isLiveTailOn && ( + { + // modifies run button to look like the update button, if there is a time change, disables timepicker setting update if timepicker is disabled + setNeedsUpdate( + !showQueryArea && // keeps statement false if using query assistant ui, timepicker shouldn't change run button + !(tRange[0] === startTime && tRange[1] === endTime) // checks to see if the time given is different from prev + ); + // keeps the time range change local, to be used when update pressed + setStartTime(tRange[0]); + setEndTime(tRange[1]); + }} + handleTimeRangePickerRefresh={() => { + onQuerySearch(queryLang); + }} + isAppAnalytics={isAppAnalytics} + /> + )} + + )} {!showQueryArea && ( diff --git a/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap b/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap index 3818564438..169620d575 100644 --- a/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap +++ b/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap @@ -971,3 +971,630 @@ exports[`Datagrid component renders data grid with different timestamp 1`] = ` `; + +exports[`Datagrid component renders data grid with no timestamp 1`] = ` + + + +
+ + + + + +
+ +
+ +
+ + 1,390 + + + + hits + +
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + + +
+
+ + + +`; diff --git a/public/components/event_analytics/explorer/__tests__/data_grid.test.tsx b/public/components/event_analytics/explorer/__tests__/data_grid.test.tsx index d9af5b86b5..5e9b4255a3 100644 --- a/public/components/event_analytics/explorer/__tests__/data_grid.test.tsx +++ b/public/components/event_analytics/explorer/__tests__/data_grid.test.tsx @@ -132,4 +132,53 @@ describe('Datagrid component', () => { expect(wrapper).toMatchSnapshot(); }); }); + + it('renders data grid with no timestamp', async () => { + const explorerFields = { + [SELECTED_FIELDS]: [], + [UNSELECTED_FIELDS]: [], + [AVAILABLE_FIELDS]: SIDEBAR_AVAILABLE_FIELDS, + [QUERIED_FIELDS]: QUERY_FIELDS, + }; + + coreStartMock.http.get = jest + .fn() + .mockResolvedValue((sampleEmptyPanel as unknown) as HttpResponse); + + const tabId = 'explorer-tab-_fbef9141-48eb-11ee-a60a-af33302cfb3c'; + + const pplService = new PPLService(coreStartMock.http); + const preloadedState = { + queries: { + [tabId]: { + [SELECTED_TIMESTAMP]: '', + }, + }, + }; + const store = configureStore({ reducer: queriesReducer, preloadedState }); + + const wrapper = mount( + + + + ); + + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); }); diff --git a/public/components/event_analytics/explorer/events_views/data_grid.tsx b/public/components/event_analytics/explorer/events_views/data_grid.tsx index b06eeffcd9..e5130215c3 100644 --- a/public/components/event_analytics/explorer/events_views/data_grid.tsx +++ b/public/components/event_analytics/explorer/events_views/data_grid.tsx @@ -12,6 +12,7 @@ import { EuiDescriptionListTitle, EuiPanel, EuiDataGridProps, + EuiSpacer, } from '@elastic/eui'; import moment from 'moment'; import React, { Fragment, MutableRefObject, useEffect, useRef, useState } from 'react'; @@ -28,6 +29,7 @@ import PPLService from '../../../../services/requests/ppl'; import { useFetchEvents } from '../../hooks'; import { redoQuery } from '../../utils/utils'; import { FlyoutButton } from './docViewRow'; +import { HitsCounter } from '../hits_counter/hits_counter'; export interface DataGridProps { http: HttpSetup; @@ -68,9 +70,11 @@ export function DataGrid(props: DataGridProps) { }); const selectedColumns = - explorerFields.selectedFields.length > 0 + explorerFields.selectedFields.length > 0 // if any fields are selected use that, otherwise defaults ? explorerFields.selectedFields - : [{ name: timeStampField, type: 'timestamp' }, ...DEFAULT_EMPTY_EXPLORER_FIELDS]; + : timeStampField && timeStampField !== '' // if theres a timestamp, include that, otherwise dont + ? [{ name: timeStampField, type: 'timestamp' }, ...DEFAULT_EMPTY_EXPLORER_FIELDS] + : DEFAULT_EMPTY_EXPLORER_FIELDS; // useRef instead of useState somehow solves the issue of user triggered sorting not // having any delays const sortingFields: MutableRefObject = useRef([]); @@ -251,6 +255,12 @@ export function DataGrid(props: DataGridProps) { return ( + {timeStampField === '' && ( + <> + {}} /> + + + )}
{ - console.log(historyFromRedirection.location.state); if (!historyFromRedirection.location.state) return; const { datasourceName, @@ -298,6 +297,7 @@ export const Explorer = ({ query: { [RAW_QUERY]: queryToRun }, }) ); + setTempQuery(queryToRun); }); }, []); @@ -541,7 +541,7 @@ export const Explorer = ({
{explorerData && !isEmpty(explorerData.jsonData) ? ( - {(isDefaultDataSourceType || appLogEvents) && ( + {(isDefaultDataSourceType || appLogEvents) && query[SELECTED_TIMESTAMP] !== '' && ( <> @@ -590,7 +590,7 @@ export const Explorer = ({ )} - {(isDefaultDataSourceType || appLogEvents) && ( + {(isDefaultDataSourceType || appLogEvents) && query[SELECTED_TIMESTAMP] !== '' && ( @@ -632,7 +632,7 @@ export const Explorer = ({ )} - {(countDistribution.data?.['count()'] || explorerData?.datarows?.length) && ( + {(explorerData?.datarows?.length || countDistribution.data?.['count()']) && ( { // get the queries isLoaded, if it exists AND is true = show no res const queryInfo = useSelector(selectQueries)[tabId]; const summaryData = useSelector(selectQueryAssistantSummarization)[tabId]; - const queryAssistLoading = summaryData.loading; + const queryAssistLoading = summaryData?.loading; return ( diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx index 70c7975bdd..0e3c1fc77c 100644 --- a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx +++ b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx @@ -50,7 +50,10 @@ import { import { TabContext, useRenderVisualization } from '../../../../../hooks'; import { DataConfigItemClickPanel } from '../config_controls/data_config_item_click_panel'; import { DataConfigPanelFields } from '../config_controls/data_config_panel_fields'; -import { composeFinalQuery } from '../../../../../../../../common/utils'; +import { + composeFinalQuery, + composeFinalQueryWithoutTimestamp, +} from '../../../../../../common/query_utils'; const initialDimensionEntry = { label: '', @@ -244,16 +247,18 @@ export const DataConfigPanelItem = ({ return { ...prevQuery, [RAW_QUERY]: newQueryString, - [FINAL_QUERY]: composeFinalQuery( - newQueryString, - prevQuery[SELECTED_DATE_RANGE][0] || 'now', - prevQuery[SELECTED_DATE_RANGE][1] || 'now', - !isEmpty(visConfig.span?.time_field) - ? visConfig.span?.time_field[0].name - : prevQuery.selectedTimestamp, - false, - '' - ), + [FINAL_QUERY]: isEmpty(prevQuery.selectedTimestamp) + ? composeFinalQueryWithoutTimestamp(newQueryString, '') + : composeFinalQuery( + newQueryString, + prevQuery[SELECTED_DATE_RANGE][0] || 'now', + prevQuery[SELECTED_DATE_RANGE][1] || 'now', + !isEmpty(visConfig.span?.time_field) + ? visConfig.span?.time_field[0].name + : prevQuery.selectedTimestamp, + false, + '' + ), }; }; @@ -299,7 +304,7 @@ export const DataConfigPanelItem = ({ }, }); }, - errorCallback: (err) => {}, + errorCallback: () => {}, }); }, [configList, query, visualizations]); @@ -312,11 +317,6 @@ export const DataConfigPanelItem = ({ fillVisDataInStore({ visData, queryState, visConfMetadata, visMeta }); }; - const isPositionButtonVisible = (sectionName: string) => - sectionName === AGGREGATIONS && - (visualizations.vis.name === VIS_CHART_TYPES.Line || - visualizations.vis.name === VIS_CHART_TYPES.Scatter); - const getTimeStampFilteredFields = (options: IField[]) => filter(options, (i: IField) => i.type !== TIMESTAMP); diff --git a/public/services/data_fetchers/ppl/ppl_data_fetcher.ts b/public/services/data_fetchers/ppl/ppl_data_fetcher.ts index 984c89984b..119747ba02 100644 --- a/public/services/data_fetchers/ppl/ppl_data_fetcher.ts +++ b/public/services/data_fetchers/ppl/ppl_data_fetcher.ts @@ -23,6 +23,7 @@ import { TAB_CHART_ID, } from '../../../../common/constants/explorer'; import { PPL_STATS_REGEX } from '../../../../common/constants/shared'; +import { composeFinalQueryWithoutTimestamp } from '../../../components/common/query_utils'; export class PPLDataFetcher extends DataFetcherBase implements IDataFetcher { protected queryIndex: string; @@ -85,28 +86,39 @@ export class PPLDataFetcher extends DataFetcherBase implements IDataFetcher { const { dispatch, changeQuery } = this.storeContext; await this.processTimestamp(query); - if (isEmpty(this.timestamp)) return; - const curStartTime = startingTime || this.query[SELECTED_DATE_RANGE][0]; - const curEndTime = endingTime || this.query[SELECTED_DATE_RANGE][1]; + const noTimestamp = isEmpty(this.timestamp); + + const curStartTime = noTimestamp + ? undefined + : startingTime || this.query[SELECTED_DATE_RANGE][0]; + const curEndTime = noTimestamp ? undefined : endingTime || this.query[SELECTED_DATE_RANGE][1]; // compose final query - const finalQuery = composeFinalQuery( - this.query[RAW_QUERY], - curStartTime, - curEndTime, - this.timestamp, - isLiveTailOn, - appBaseQuery, - this.query[SELECTED_PATTERN_FIELD], - this.query[PATTERN_REGEX], - this.query[FILTERED_PATTERN] - ); + const finalQuery = noTimestamp + ? composeFinalQueryWithoutTimestamp( + this.query[RAW_QUERY], + appBaseQuery, + this.query[SELECTED_PATTERN_FIELD], + this.query[PATTERN_REGEX], + this.query[FILTERED_PATTERN] + ) + : composeFinalQuery( + this.query[RAW_QUERY], + curStartTime, + curEndTime, + this.timestamp, + isLiveTailOn, + appBaseQuery, + this.query[SELECTED_PATTERN_FIELD], + this.query[PATTERN_REGEX], + this.query[FILTERED_PATTERN] + ); // update UI with new query state await this.updateQueryState(this.query[RAW_QUERY], finalQuery, this.timestamp); // calculate proper time interval for count distribution - if (!selectedInterval.current || selectedInterval.current.text === 'Auto') { + if (!noTimestamp && (!selectedInterval.current || selectedInterval.current.text === 'Auto')) { findAutoInterval(curStartTime, curEndTime); } diff --git a/public/services/saved_objects/saved_object_loaders/explorer_saved_object_loader.ts b/public/services/saved_objects/saved_object_loaders/explorer_saved_object_loader.ts index a33b65201c..f874698120 100644 --- a/public/services/saved_objects/saved_object_loaders/explorer_saved_object_loader.ts +++ b/public/services/saved_objects/saved_object_loaders/explorer_saved_object_loader.ts @@ -204,7 +204,7 @@ export class ExplorerSavedObjectLoader extends SavedObjectLoaderBase implements tabId, query: { [RAW_QUERY]: currQuery, - [SELECTED_TIMESTAMP]: objectData?.selected_timestamp?.name || 'timestamp', + [SELECTED_TIMESTAMP]: objectData?.selected_timestamp?.name, [SAVED_OBJECT_ID]: objectId, [SAVED_OBJECT_TYPE]: savedType, [SELECTED_DATE_RANGE]: