Skip to content

Commit

Permalink
Add initial DiscoverChart
Browse files Browse the repository at this point in the history
DiscoverChart contains HitsCounter, TimeChartHeader and Histogram in the previous
discover_legacy.

Issue Resolved:
opensearch-project#4571

Signed-off-by: ananzh <ananzh@amazon.com>
  • Loading branch information
ananzh committed Jul 26, 2023
1 parent 146cc36 commit e8dd409
Show file tree
Hide file tree
Showing 15 changed files with 549 additions and 97 deletions.
101 changes: 101 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,101 @@
/*
* 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 { SavedSearch } from '../../../saved_searches';
import { HitsCounter } from '../hits_counter';
import { TimechartHeader } from '../timechart_header';
import { DiscoverHistogram } from './histogram';
import { DiscoverServices } from '../../../build_services';
interface DiscoverChartProps {
bucketInterval: any;
chartData: any;
config: IUiSettingsClient;
data: DataPublicPluginStart;
hits: number;
resetQuery: () => void;
savedSearch: SavedSearch;
timeField?: string;
services: DiscoverServices;
}

export const DiscoverChart = ({
bucketInterval,
chartData,
config,
data,
hits,
resetQuery,
savedSearch,
timeField,
services,
}: 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 onChangeInterval = () => {};

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={!!(savedSearch && savedSearch.id)}
onResetQuery={resetQuery}
/>
</EuiFlexItem>
{timeField && (
<EuiFlexItem className="dscChart__TimechartHeader">
<TimechartHeader
bucketInterval={bucketInterval}
dateFormat={config.get('dateFormat')}
timeRange={timeRange}
options={search.aggs.intervalOptions}
onChangeInterval={onChangeInterval}
stateInterval={'auto'}
/>
</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
Expand Up @@ -58,12 +58,13 @@ import { i18n } from '@osd/i18n';
import { IUiSettingsClient } from 'opensearch-dashboards/public';
import { EuiChartThemeType } from '@elastic/eui/dist/eui_charts_theme';
import { Subscription, combineLatest } from 'rxjs';
import { getServices } from '../../../opensearch_dashboards_services';
import { Chart as IChart } from '../helpers/point_series';
import { Chart as IChart } from './utils/point_series';
import { DiscoverServices } from '../../../build_services';

export interface DiscoverHistogramProps {
chartData: IChart;
timefilterUpdateHandler: (ranges: { from: number; to: number }) => void;
services: DiscoverServices;
}

interface DiscoverHistogramState {
Expand Down Expand Up @@ -140,15 +141,11 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
};

private subscription?: Subscription;
public state = {
chartsTheme: getServices().theme.chartsDefaultTheme,
chartsBaseTheme: getServices().theme.chartsDefaultBaseTheme,
};

componentDidMount() {
this.subscription = combineLatest(
getServices().theme.chartsTheme$,
getServices().theme.chartsBaseTheme$
this.props.services.theme.chartsTheme$,
this.props.services.theme.chartsBaseTheme$
).subscribe(([chartsTheme, chartsBaseTheme]) =>
this.setState({ chartsTheme, chartsBaseTheme })
);
Expand Down Expand Up @@ -220,10 +217,11 @@ export class DiscoverHistogram extends Component<DiscoverHistogramProps, Discove
};

public render() {
const uiSettings = getServices().uiSettings;
const { chartData, services } = this.props;
const { uiSettings } = services;
const timeZone = getTimezone(uiSettings);
const { chartData } = this.props;
const { chartsTheme, chartsBaseTheme } = this.state;
const chartsTheme = services.theme.chartsDefaultTheme;
const chartsBaseTheme = services.theme.chartsDefaultBaseTheme;

if (!chartData) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { DataPublicPluginStart, IndexPattern } from '../../../../../../data/public';

export function createHistogramConfigs(
indexPattern: IndexPattern,
histogramInterval: string,
data: DataPublicPluginStart
) {
const visStateAggs = [
{
type: 'count',
schema: 'metric',
},
{
type: 'date_histogram',
schema: 'segment',
params: {
field: indexPattern.timeFieldName!,
interval: histogramInterval,
timeRange: data.query.timefilter.timefilter.getTime(),
},
},
];
return data.search.aggs.createAggConfigs(indexPattern, visStateAggs);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Any modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import moment from 'moment';
import dateMath from '@elastic/datemath';
import { IAggConfigs } from '../../../../../../data/common';
import { search } from '../../../../../../data/public';

export function getDimensions(aggs: IAggConfigs, data: any) {
const [metric, agg] = aggs.aggs;
const { from, to } = data.query.timefilter.timefilter.getTime();
agg.params.timeRange = {
from: dateMath.parse(from),
to: dateMath.parse(to, { roundUp: true }),
};
const bounds = agg.params.timeRange
? data.query.timefilter.timefilter.calculateBounds(agg.params.timeRange)
: null;
const buckets = search.aggs.isDateHistogramBucketAggConfig(agg) ? agg.buckets : undefined;

if (!buckets || !bounds) {
return;
}

const { opensearchUnit, opensearchValue } = buckets.getInterval();
return {
x: {
accessor: 0,
label: agg.makeLabel(),
format: agg.toSerializedFieldFormat(),
params: {
date: true,
interval: moment.duration(opensearchValue, opensearchUnit),
intervalOpenSearchValue: opensearchValue,
intervalOpenSearchUnit: opensearchUnit,
format: buckets.getScaledDateFormat(),
bounds: buckets.getBounds(),
},
},
y: {
accessor: 1,
format: metric.toSerializedFieldFormat(),
label: metric.makeLabel(),
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './create_histogram_configs';
export * from './point_series';
export * from './get_dimensions';
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Any modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { uniq } from 'lodash';
import { Duration, Moment } from 'moment';
import { Unit } from '@elastic/datemath';

import { SerializedFieldFormat } from '../../../../../../expressions/common/types';

export interface Column {
id: string;
name: string;
}

export interface Row {
[key: string]: number | 'NaN';
}

export interface Table {
columns: Column[];
rows: Row[];
}

interface HistogramParams {
date: true;
interval: Duration;
intervalOpenSearchValue: number;
intervalOpenSearchUnit: Unit;
format: string;
bounds: {
min: Moment;
max: Moment;
};
}
export interface Dimension {
accessor: 0 | 1;
format: SerializedFieldFormat<{ pattern: string }>;
}

export interface Dimensions {
x: Dimension & { params: HistogramParams };
y: Dimension;
}

interface Ordered {
date: true;
interval: Duration;
intervalOpenSearchUnit: string;
intervalOpenSearchValue: number;
min: Moment;
max: Moment;
}
export interface Chart {
values: Array<{
x: number;
y: number;
}>;
xAxisOrderedValues: number[];
xAxisFormat: Dimension['format'];
xAxisLabel: Column['name'];
yAxisLabel?: Column['name'];
ordered: Ordered;
}

export const buildPointSeriesData = (table: Table, dimensions: Dimensions) => {
const { x, y } = dimensions;
const xAccessor = table.columns[x.accessor].id;
const yAccessor = table.columns[y.accessor].id;
const chart = {} as Chart;

chart.xAxisOrderedValues = uniq(table.rows.map((r) => r[xAccessor] as number));
chart.xAxisFormat = x.format;
chart.xAxisLabel = table.columns[x.accessor].name;

const { intervalOpenSearchUnit, intervalOpenSearchValue, interval, bounds } = x.params;
chart.ordered = {
date: true,
interval,
intervalOpenSearchUnit,
intervalOpenSearchValue,
min: bounds.min,
max: bounds.max,
};

chart.yAxisLabel = table.columns[y.accessor].name;

chart.values = table.rows
.filter((row) => row && row[yAccessor] !== 'NaN')
.map((row) => ({
x: row[xAccessor] as number,
y: row[yAccessor] as number,
}));

return chart;
};
Loading

0 comments on commit e8dd409

Please sign in to comment.