Skip to content

Commit

Permalink
[Data Explorer][Discover 2.0] Add missing change interval on TimeChar…
Browse files Browse the repository at this point in the history
…t and improve fetch

Issue Resolve
opensearch-project#4847

Signed-off-by: ananzh <ananzh@amazon.com>
  • Loading branch information
ananzh committed Aug 29, 2023
1 parent 0188087 commit 42a3123
Show file tree
Hide file tree
Showing 6 changed files with 780 additions and 0 deletions.
108 changes: 108 additions & 0 deletions src/plugins/discover/public/application/components/chart/chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useCallback } from 'react';
import moment from 'moment';
import dateMath from '@elastic/datemath';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { IUiSettingsClient } from 'opensearch-dashboards/public';
import { DataPublicPluginStart, search } from '../../../../../data/public';
import { HitsCounter } from './hits_counter';
import { TimechartHeader, TimechartHeaderBucketInterval } from './timechart_header';
import { DiscoverHistogram } from './histogram/histogram';
import { DiscoverServices } from '../../../build_services';
import { Chart } from './utils';
import { setInterval, useDispatch, useSelector } from '../../utils/state_management';

interface DiscoverChartProps {
bucketInterval: TimechartHeaderBucketInterval;
chartData: Chart;
config: IUiSettingsClient;
data: DataPublicPluginStart;
hits: number;
resetQuery: () => void;
showResetButton?: boolean;
timeField?: string;
services: DiscoverServices;
onRefresh: () => void;
}

export const DiscoverChart = ({
bucketInterval,
chartData,
config,
data,
hits,
resetQuery,
timeField,
services,
showResetButton = false,
onRefresh,
}: DiscoverChartProps) => {
const { from, to } = data.query.timefilter.timefilter.getTime();
const timeRange = {
from: dateMath.parse(from)?.format('YYYY-MM-DDTHH:mm:ss.SSSZ') || '',
to: dateMath.parse(to, { roundUp: true })?.format('YYYY-MM-DDTHH:mm:ss.SSSZ') || '',
};
const { interval } = useSelector((state) => state.discover);
const dispatch = useDispatch();
const onChangeInterval = (newInterval: string) => {
dispatch(setInterval(newInterval));
onRefresh();
};
const timefilterUpdateHandler = useCallback(
(ranges: { from: number; to: number }) => {
data.query.timefilter.timefilter.setTime({
from: moment(ranges.from).toISOString(),
to: moment(ranges.to).toISOString(),
mode: 'absolute',
});
},
[data]
);

return (
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem grow={false} className="dscChart__hitsCounter">
<HitsCounter
hits={hits > 0 ? hits : 0}
showResetButton={showResetButton}
onResetQuery={resetQuery}
/>
</EuiFlexItem>
{timeField && (
<EuiFlexItem className="dscChart__TimechartHeader">
<TimechartHeader
bucketInterval={bucketInterval}
dateFormat={config.get('dateFormat')}
timeRange={timeRange}
options={search.aggs.intervalOptions}
onChangeInterval={onChangeInterval}
stateInterval={interval || ''}
/>
</EuiFlexItem>
)}
{timeField && chartData && (
<EuiFlexItem grow={false}>
<section
aria-label={i18n.translate('discover.histogramOfFoundDocumentsAriaLabel', {
defaultMessage: 'Histogram of found documents',
})}
className="dscTimechart"
>
<div className="dscHistogram" data-test-subj="discoverChart">
<DiscoverHistogram
chartData={chartData}
timefilterUpdateHandler={timefilterUpdateHandler}
services={services}
/>
</div>
</section>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { matchPath } from 'react-router-dom';
import { Filter, Query } from '../../../../../data/public';
import { DiscoverServices } from '../../../build_services';
import { RootState, DefaultViewState } from '../../../../../data_explorer/public';
import { buildColumns } from '../columns';
import * as utils from './common';
import { SortOrder } from '../../../saved_searches/types';

export interface DiscoverState {
/**
* Columns displayed in the table
*/
columns: string[];
/**
* Array of applied filters
*/
filters?: Filter[];
/**
* Used interval of the histogram
*/
interval?: string;
/**
* Lucence or DQL query
*/
query?: Query;
/**
* Array of the used sorting [[field,direction],...]
*/
sort: SortOrder[];
/**
* id of the used saved search
*/
savedSearch?: string;
/**
* dirty flag to indicate if the saved search has been modified
* since the last save
*/
isDirty: boolean;
}

export interface DiscoverRootState extends RootState {
discover: DiscoverState;
}

const initialState: DiscoverState = {
columns: ['_source'],
sort: [],
isDirty: false,
};

export const getPreloadedState = async ({
getSavedSearchById,
}: DiscoverServices): Promise<DefaultViewState<DiscoverState>> => {
const preloadedState: DefaultViewState<DiscoverState> = {
state: {
...initialState,
},
};

const hashPath = window.location.hash.split('?')[0]; // hack to remove query params since matchPath considers them part of the id
const savedSearchId = matchPath<{ id?: string }>(hashPath, {
path: '#/view/:id',
})?.params.id;

if (savedSearchId) {
const savedSearchInstance = await getSavedSearchById(savedSearchId);

if (savedSearchInstance) {
preloadedState.state.columns = savedSearchInstance.columns;
preloadedState.state.sort = savedSearchInstance.sort;
preloadedState.state.savedSearch = savedSearchInstance.id;
const indexPatternId = savedSearchInstance.searchSource.getField('index')?.id;
preloadedState.root = {
metadata: {
indexPattern: indexPatternId,
},
};

savedSearchInstance.destroy(); // this instance is no longer needed, will create another one later
}
}

return preloadedState;
};

export const discoverSlice = createSlice({
name: 'discover',
initialState,
reducers: {
setState(state, action: PayloadAction<DiscoverState>) {
return action.payload;
},
addColumn(state, action: PayloadAction<{ column: string; index?: number }>) {
const columns = utils.addColumn(state.columns || [], action.payload);
return { ...state, columns: buildColumns(columns) };
},
removeColumn(state, action: PayloadAction<string>) {
const columns = utils.removeColumn(state.columns, action.payload);
const sort =
state.sort && state.sort.length ? state.sort.filter((s) => s[0] !== action.payload) : [];
return {
...state,
columns: buildColumns(columns),
sort,
isDirty: true,
};
},
reorderColumn(state, action: PayloadAction<{ source: number; destination: number }>) {
const columns = utils.reorderColumn(
state.columns,
action.payload.source,
action.payload.destination
);
return {
...state,
columns,
isDirty: true,
};
},
setColumns(state, action: PayloadAction<{ timeField: string | undefined; columns: string[] }>) {
const columns = utils.setColumns(action.payload.timeField, action.payload.columns);
return {
...state,
columns,
};
},
setSort(state, action: PayloadAction<SortOrder[]>) {
return {
...state,
sort: action.payload,
};
},
setInterval(state, action: PayloadAction<string>) {
return {
...state,
interval: action.payload,
};
},
updateState(state, action: PayloadAction<Partial<DiscoverState>>) {
return {
...state,
...action.payload,
};
},
setSavedSearchId(state, action: PayloadAction<string>) {
return {
...state,
savedSearch: action.payload,
isDirty: false,
};
},
},
});

// Exposing the state functions as generics
export const {
addColumn,
removeColumn,
reorderColumn,
setColumns,
setSort,
setInterval,
setState,
updateState,
setSavedSearchId,
} = discoverSlice.actions;
export const { reducer } = discoverSlice;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import './discover_chart_container.scss';
import React from 'react';
import { DiscoverViewServices } from '../../../build_services';
import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public';
import { useDiscoverContext } from '../context';
import { SearchData } from '../utils/use_search';
import { DiscoverChart } from '../../components/chart/chart';

export const DiscoverChartContainer = (
{ hits, bucketInterval, chartData }: SearchData,
onRefresh: () => void
) => {
const { services } = useOpenSearchDashboards<DiscoverViewServices>();
const { uiSettings, data } = services;
const { indexPattern, savedSearch } = useDiscoverContext();

const timeField = indexPattern?.timeFieldName;

if (!hits || !bucketInterval || !chartData) {
// TODO: handle better
return null;
}

return (
<DiscoverChart
bucketInterval={bucketInterval}
chartData={chartData}
config={uiSettings}
data={data}
hits={hits}
timeField={timeField}
resetQuery={() => {
window.location.href = `#/view/${savedSearch?.id}`;
window.location.reload();
}}
services={services}
showResetButton={!!savedSearch && !!savedSearch.id}
onRefresh={onRefresh}
/>
);
};
Loading

0 comments on commit 42a3123

Please sign in to comment.