Skip to content

Commit

Permalink
Use ESQL for displaying the histogram (#163780)
Browse files Browse the repository at this point in the history
## Summary

Uses ESQL for displaying the histogram

**This will be merged in the feature branch**
  • Loading branch information
stratoula authored Aug 14, 2023
1 parent e8c1e7a commit 9b2acc2
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 22 deletions.
4 changes: 3 additions & 1 deletion src/plugins/unified_histogram/public/chart/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export interface ChartProps {
disabledActions?: LensEmbeddableInput['disabledActions'];
input$?: UnifiedHistogramInput$;
lensTablesAdapter?: Record<string, Datatable>;
isOnHistogramMode?: boolean;
onResetChartHeight?: () => void;
onChartHiddenChange?: (chartHidden: boolean) => void;
onTimeIntervalChange?: (timeInterval: string) => void;
Expand Down Expand Up @@ -104,6 +105,7 @@ export function Chart({
disabledActions,
input$: originalInput$,
lensTablesAdapter,
isOnHistogramMode,
onResetChartHeight,
onChartHiddenChange,
onTimeIntervalChange,
Expand Down Expand Up @@ -425,7 +427,7 @@ export function Chart({
disableTriggers={disableTriggers}
disabledActions={disabledActions}
onTotalHitsChange={onTotalHitsChange}
hasLensSuggestions={Boolean(currentSuggestion)}
hasLensSuggestions={!Boolean(isOnHistogramMode)}
onChartLoad={onChartLoad}
onFilter={onFilter}
onBrushEnd={onBrushEnd}
Expand Down
29 changes: 24 additions & 5 deletions src/plugins/unified_histogram/public/chart/histogram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useEuiTheme, useResizeObserver } from '@elastic/eui';
import { css } from '@emotion/react';
import React, { useState, useRef, useEffect } from 'react';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common';
import type { DefaultInspectorAdapters, Datatable } from '@kbn/expressions-plugin/common';
import type { IKibanaSearchResponse } from '@kbn/data-plugin/public';
import type { estypes } from '@elastic/elasticsearch';
import type { TimeRange } from '@kbn/es-query';
Expand Down Expand Up @@ -52,6 +52,28 @@ export interface HistogramProps {
onBrushEnd?: LensEmbeddableInput['onBrushEnd'];
}

const computeTotalHits = (
hasLensSuggestions: boolean,
adapterTables:
| {
[key: string]: Datatable;
}
| undefined,
isPlainRecord?: boolean
) => {
if (isPlainRecord && hasLensSuggestions) {
return Object.values(adapterTables ?? {})?.[0]?.rows?.length;
} else if (isPlainRecord && !hasLensSuggestions) {
let rowsCount = 0;
Object.values(adapterTables ?? {})?.[0]?.rows.forEach((r) => {
rowsCount += r.rows;
});
return rowsCount;
} else {
return adapterTables?.unifiedHistogram?.meta?.statistics?.totalCount;
}
};

export function Histogram({
services: { data, lens, uiSettings },
dataView,
Expand Down Expand Up @@ -111,10 +133,7 @@ export function Histogram({
}

const adapterTables = adapters?.tables?.tables;
const totalHits =
isPlainRecord && hasLensSuggestions
? Object.values(adapterTables ?? {})?.[0]?.rows?.length
: adapterTables?.unifiedHistogram?.meta?.statistics?.totalCount;
const totalHits = computeTotalHits(hasLensSuggestions, adapterTables, isPlainRecord);

onTotalHitsChange?.(
isLoading ? UnifiedHistogramFetchStatus.loading : UnifiedHistogramFetchStatus.complete,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,75 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { DataView } from '@kbn/data-views-plugin/common';
import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import {
AggregateQuery,
isOfAggregateQueryType,
Query,
TimeRange,
getAggregateQueryMode,
} from '@kbn/es-query';
import type { DatatableColumn } from '@kbn/expressions-plugin/common';
import { LensSuggestionsApi, Suggestion } from '@kbn/lens-plugin/public';
import { isEqual } from 'lodash';
import { useEffect, useMemo, useRef, useState } from 'react';

const roundInterval = (interval: number) => {
{
switch (true) {
case interval <= 500: // <= 0.5s
return '100 millisecond';
case interval <= 5000: // <= 5s
return '1 second';
case interval <= 7500: // <= 7.5s
return '5 second';
case interval <= 15000: // <= 15s
return '10 second';
case interval <= 45000: // <= 45s
return '30 second';
case interval <= 180000: // <= 3m
return '1 minute';
case interval <= 450000: // <= 9m
return '5 minute';
case interval <= 1200000: // <= 20m
return '10 minute';
case interval <= 2700000: // <= 45m
return '30 minute';
case interval <= 7200000: // <= 2h
return '1 hour';
case interval <= 21600000: // <= 6h
return '3 hour';
case interval <= 86400000: // <= 24h
return '12 hour';
case interval <= 604800000: // <= 1w
return '24 hour';
case interval <= 1814400000: // <= 3w
return '1 week';
case interval < 3628800000: // < 2y
return '30 day';
default:
return '1 year';
}
}
};

const computeInterval = (timeRange: TimeRange, data: DataPublicPluginStart): string => {
const bounds = data.query.timefilter.timefilter.calculateBounds(timeRange!);
const min = bounds.min!.valueOf();
const max = bounds.max!.valueOf();
const interval = (max - min) / 50;
return roundInterval(interval);
};

export const useLensSuggestions = ({
dataView,
query,
originalSuggestion,
isPlainRecord,
columns,
timeRange,
data,
lensSuggestionsApi,
onSuggestionChange,
}: {
Expand All @@ -27,6 +82,8 @@ export const useLensSuggestions = ({
originalSuggestion?: Suggestion;
isPlainRecord?: boolean;
columns?: DatatableColumn[];
timeRange?: TimeRange;
data: DataPublicPluginStart;
lensSuggestionsApi: LensSuggestionsApi;
onSuggestionChange?: (suggestion: Suggestion | undefined) => void;
}) => {
Expand All @@ -47,8 +104,51 @@ export const useLensSuggestions = ({
}, [dataView, isPlainRecord, lensSuggestionsApi, query, columns]);

const [allSuggestions, setAllSuggestions] = useState(suggestions.allSuggestions);
const currentSuggestion = originalSuggestion ?? suggestions.firstSuggestion;
let currentSuggestion = originalSuggestion ?? suggestions.firstSuggestion;
const suggestionDeps = useRef(getSuggestionDeps({ dataView, query, columns }));
let isOnHistogramMode = false;

if (
!currentSuggestion &&
dataView.isTimeBased() &&
query &&
isOfAggregateQueryType(query) &&
getAggregateQueryMode(query) === 'esql' &&
timeRange
) {
const language = getAggregateQueryMode(query);
const interval = computeInterval(timeRange, data);
const histogramQuery = `${query[language]} | eval uniqueName = 1
| EVAL timestamp=DATE_TRUNC(${dataView.timeFieldName}, ${interval}) | stats rows = count(uniqueName) by timestamp | rename timestamp as \`${dataView.timeFieldName} every ${interval}\``;
const context = {
dataViewSpec: dataView?.toSpec(),
fieldName: '',
textBasedColumns: [
{
id: `${dataView.timeFieldName} every ${interval}`,
name: `${dataView.timeFieldName} every ${interval}`,
meta: {
type: 'date',
},
},
{
id: 'rows',
name: 'rows',
meta: {
type: 'number',
},
},
] as DatatableColumn[],
query: {
esql: histogramQuery,
},
};
const sug = isPlainRecord ? lensSuggestionsApi(context, dataView, ['lnsDatatable']) ?? [] : [];
if (sug.length) {
currentSuggestion = sug[0];
isOnHistogramMode = true;
}
}

useEffect(() => {
const newSuggestionsDeps = getSuggestionDeps({ dataView, query, columns });
Expand All @@ -71,7 +171,8 @@ export const useLensSuggestions = ({
return {
allSuggestions,
currentSuggestion,
suggestionUnsupported: !currentSuggestion && !dataView.isTimeBased(),
suggestionUnsupported: isPlainRecord && !currentSuggestion,
isOnHistogramMode,
};
};

Expand Down
22 changes: 13 additions & 9 deletions src/plugins/unified_histogram/public/layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,18 @@ export const UnifiedHistogramLayout = ({
onBrushEnd,
children,
}: UnifiedHistogramLayoutProps) => {
const { allSuggestions, currentSuggestion, suggestionUnsupported } = useLensSuggestions({
dataView,
query,
originalSuggestion,
isPlainRecord,
columns,
lensSuggestionsApi,
onSuggestionChange,
});
const { allSuggestions, currentSuggestion, suggestionUnsupported, isOnHistogramMode } =
useLensSuggestions({
dataView,
query,
originalSuggestion,
isPlainRecord,
columns,
timeRange,
data: services.data,
lensSuggestionsApi,
onSuggestionChange,
});

const chart = suggestionUnsupported ? undefined : originalChart;

Expand Down Expand Up @@ -277,6 +280,7 @@ export const UnifiedHistogramLayout = ({
onFilter={onFilter}
onBrushEnd={onBrushEnd}
lensTablesAdapter={lensTablesAdapter}
isOnHistogramMode={isOnHistogramMode}
/>
</InPortal>
<InPortal node={mainPanelNode}>{children}</InPortal>
Expand Down
4 changes: 2 additions & 2 deletions test/functional/apps/discover/group2/_sql_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await testSubjects.exists('showQueryBarMenu')).to.be(false);
expect(await testSubjects.exists('addFilter')).to.be(false);
expect(await testSubjects.exists('dscViewModeDocumentButton')).to.be(false);
// when Lens suggests a table, we render the histogram
expect(await testSubjects.exists('unifiedHistogramChart')).to.be(true);
// when Lens suggests a table, we render an ESQL based histogram
expect(await testSubjects.exists('unifiedHistogramChart')).to.be(false);
expect(await testSubjects.exists('unifiedHistogramQueryHits')).to.be(true);
expect(await testSubjects.exists('discoverAlertsButton')).to.be(false);
expect(await testSubjects.exists('shareTopNavButton')).to.be(true);
Expand Down
2 changes: 1 addition & 1 deletion test/functional/apps/discover/group3/_request_counts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});

describe('SQL mode', () => {
describe.skip('SQL mode', () => {
const type = 'sql';

beforeEach(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {

expect(searchesCountBeforeRestore).to.be(searchesCountAfterRestore); // no new searches started during restore
});

it('should should clean the search session when navigating to SQL mode, and reinitialize when navigating back', async () => {
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setDefaultAbsoluteRange();
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await searchSessions.exists()).to.be(true);
await PageObjects.discover.selectTextBaseLang('SQL');
await PageObjects.header.waitUntilLoadingHasFinished();
await searchSessions.missingOrFail();
await browser.goBack();
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await searchSessions.exists()).to.be(true);
});
});

async function getSearchSessionId(): Promise<string> {
Expand Down

0 comments on commit 9b2acc2

Please sign in to comment.