mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Fixes error when viewing rollup v1 index patterns (#118019)
* Fixes error when viewing rollup v1 index patterns * Fix time based data view detection Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
afa2392e72
commit
61acc0db7d
15 changed files with 77 additions and 48 deletions
|
@ -14,7 +14,7 @@ import { SearchSource } from './search_source';
|
|||
import { ISearchStartSearchSource, ISearchSource, SearchSourceFields } from './types';
|
||||
|
||||
export const searchSourceInstanceMock: MockedKeys<ISearchSource> = {
|
||||
setPreferredSearchStrategyId: jest.fn(),
|
||||
setOverwriteDataViewType: jest.fn(),
|
||||
setFields: jest.fn().mockReturnThis(),
|
||||
setField: jest.fn().mockReturnThis(),
|
||||
removeField: jest.fn().mockReturnThis(),
|
||||
|
|
|
@ -124,7 +124,8 @@ export interface SearchSourceDependencies extends FetchHandlers {
|
|||
/** @public **/
|
||||
export class SearchSource {
|
||||
private id: string = uniqueId('data_source');
|
||||
private searchStrategyId?: string;
|
||||
private shouldOverwriteDataViewType: boolean = false;
|
||||
private overwriteDataViewType?: string;
|
||||
private parent?: SearchSource;
|
||||
private requestStartHandlers: Array<
|
||||
(searchSource: SearchSource, options?: ISearchOptions) => Promise<unknown>
|
||||
|
@ -149,11 +150,22 @@ export class SearchSource {
|
|||
*****/
|
||||
|
||||
/**
|
||||
* internal, dont use
|
||||
* @param searchStrategyId
|
||||
* Used to make the search source overwrite the actual data view type for the
|
||||
* specific requests done. This should only be needed very rarely, since it means
|
||||
* e.g. we'd be treating a rollup index pattern as a regular one. Be very sure
|
||||
* you understand the consequences of using this method before using it.
|
||||
*
|
||||
* @param overwriteType If `false` is passed in it will disable the overwrite, otherwise
|
||||
* the passed in value will be used as the data view type for this search source.
|
||||
*/
|
||||
setPreferredSearchStrategyId(searchStrategyId: string) {
|
||||
this.searchStrategyId = searchStrategyId;
|
||||
setOverwriteDataViewType(overwriteType: string | undefined | false) {
|
||||
if (overwriteType === false) {
|
||||
this.shouldOverwriteDataViewType = false;
|
||||
this.overwriteDataViewType = undefined;
|
||||
} else {
|
||||
this.shouldOverwriteDataViewType = true;
|
||||
this.overwriteDataViewType = overwriteType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -609,11 +621,7 @@ export class SearchSource {
|
|||
}
|
||||
|
||||
private getIndexType(index?: IIndexPattern) {
|
||||
if (this.searchStrategyId) {
|
||||
return this.searchStrategyId === 'default' ? undefined : this.searchStrategyId;
|
||||
} else {
|
||||
return index?.type;
|
||||
}
|
||||
return this.shouldOverwriteDataViewType ? this.overwriteDataViewType : index?.type;
|
||||
}
|
||||
|
||||
private readonly getFieldName = (fld: string | Record<string, any>): string =>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { VIEW_MODE } from '../../../../components/view_mode_toggle';
|
|||
|
||||
setHeaderActionMenuMounter(jest.fn());
|
||||
|
||||
function getProps(timefield?: string) {
|
||||
function getProps(isTimeBased: boolean = false) {
|
||||
const searchSourceMock = createSearchSourceMock({});
|
||||
const services = discoverServiceMock;
|
||||
services.data.query.timefilter.timefilter.getAbsoluteTime = () => {
|
||||
|
@ -85,6 +85,7 @@ function getProps(timefield?: string) {
|
|||
}) as DataCharts$;
|
||||
|
||||
return {
|
||||
isTimeBased,
|
||||
resetSavedSearch: jest.fn(),
|
||||
savedSearch: savedSearchMock,
|
||||
savedSearchDataChart$: charts$,
|
||||
|
@ -94,7 +95,6 @@ function getProps(timefield?: string) {
|
|||
services,
|
||||
state: { columns: [] },
|
||||
stateContainer: {} as GetStateReturn,
|
||||
timefield,
|
||||
viewMode: VIEW_MODE.DOCUMENT_LEVEL,
|
||||
setDiscoverViewMode: jest.fn(),
|
||||
};
|
||||
|
@ -106,7 +106,7 @@ describe('Discover chart', () => {
|
|||
expect(component.find('[data-test-subj="discoverChartOptionsToggle"]').exists()).toBeFalsy();
|
||||
});
|
||||
test('render with filefield', () => {
|
||||
const component = mountWithIntl(<DiscoverChart {...getProps('timefield')} />);
|
||||
const component = mountWithIntl(<DiscoverChart {...getProps(true)} />);
|
||||
expect(component.find('[data-test-subj="discoverChartOptionsToggle"]').exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ export function DiscoverChart({
|
|||
services,
|
||||
state,
|
||||
stateContainer,
|
||||
timefield,
|
||||
isTimeBased,
|
||||
viewMode,
|
||||
setDiscoverViewMode,
|
||||
}: {
|
||||
|
@ -48,7 +48,7 @@ export function DiscoverChart({
|
|||
services: DiscoverServices;
|
||||
state: AppState;
|
||||
stateContainer: GetStateReturn;
|
||||
timefield?: string;
|
||||
isTimeBased: boolean;
|
||||
viewMode: VIEW_MODE;
|
||||
setDiscoverViewMode: (viewMode: VIEW_MODE) => void;
|
||||
}) {
|
||||
|
@ -123,7 +123,7 @@ export function DiscoverChart({
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{timefield && (
|
||||
{isTimeBased && (
|
||||
<EuiFlexItem className="dscResultCount__toggle" grow={false}>
|
||||
<EuiPopover
|
||||
id="dscChartOptions"
|
||||
|
@ -150,7 +150,7 @@ export function DiscoverChart({
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
{timefield && !state.hideChart && (
|
||||
{isTimeBased && !state.hideChart && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<section
|
||||
ref={(element) => (chartRef.current.element = element)}
|
||||
|
|
|
@ -49,6 +49,7 @@ import {
|
|||
DOCUMENTS_VIEW_CLICK,
|
||||
FIELD_STATISTICS_VIEW_CLICK,
|
||||
} from '../../../components/field_stats_table/constants';
|
||||
import { DataViewType } from '../../../../../../data_views/common';
|
||||
|
||||
/**
|
||||
* Local storage key for sidebar persistence state
|
||||
|
@ -122,8 +123,12 @@ export function DiscoverLayout({
|
|||
|
||||
useSavedSearchAliasMatchRedirect({ savedSearch, spaces, history });
|
||||
|
||||
const timeField = useMemo(() => {
|
||||
return indexPattern.type !== 'rollup' ? indexPattern.timeFieldName : undefined;
|
||||
// We treat rollup v1 data views as non time based in Discover, since we query them
|
||||
// in a non time based way using the regular _search API, since the internal
|
||||
// representation of those documents does not have the time field that _field_caps
|
||||
// reports us.
|
||||
const isTimeBased = useMemo(() => {
|
||||
return indexPattern.type !== DataViewType.ROLLUP && indexPattern.isTimeBased();
|
||||
}, [indexPattern]);
|
||||
|
||||
const initialSidebarClosed = Boolean(storage.get(SIDEBAR_CLOSED_KEY));
|
||||
|
@ -276,7 +281,7 @@ export function DiscoverLayout({
|
|||
>
|
||||
{resultState === 'none' && (
|
||||
<DiscoverNoResults
|
||||
timeFieldName={timeField}
|
||||
isTimeBased={isTimeBased}
|
||||
data={data}
|
||||
error={dataState.error}
|
||||
hasQuery={!!state.query?.query}
|
||||
|
@ -307,7 +312,7 @@ export function DiscoverLayout({
|
|||
savedSearchDataTotalHits$={totalHits$}
|
||||
services={services}
|
||||
stateContainer={stateContainer}
|
||||
timefield={timeField}
|
||||
isTimeBased={isTimeBased}
|
||||
viewMode={viewMode}
|
||||
setDiscoverViewMode={setDiscoverViewMode}
|
||||
/>
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('DiscoverNoResults', () => {
|
|||
describe('timeFieldName', () => {
|
||||
test('renders time range feedback', () => {
|
||||
const result = mountAndFindSubjects({
|
||||
timeFieldName: 'awesome_time_field',
|
||||
isTimeBased: true,
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -94,7 +94,7 @@ describe('DiscoverNoResults', () => {
|
|||
test('renders error message', () => {
|
||||
const error = new Error('Fatal error');
|
||||
const result = mountAndFindSubjects({
|
||||
timeFieldName: 'awesome_time_field',
|
||||
isTimeBased: true,
|
||||
error,
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
|
|
@ -22,7 +22,7 @@ import './_no_results.scss';
|
|||
import { NoResultsIllustration } from './assets/no_results_illustration';
|
||||
|
||||
export interface DiscoverNoResultsProps {
|
||||
timeFieldName?: string;
|
||||
isTimeBased?: boolean;
|
||||
error?: Error;
|
||||
data?: DataPublicPluginStart;
|
||||
hasQuery?: boolean;
|
||||
|
@ -31,7 +31,7 @@ export interface DiscoverNoResultsProps {
|
|||
}
|
||||
|
||||
export function DiscoverNoResults({
|
||||
timeFieldName,
|
||||
isTimeBased,
|
||||
error,
|
||||
data,
|
||||
hasFilters,
|
||||
|
@ -54,7 +54,7 @@ export function DiscoverNoResults({
|
|||
<NoResultsIllustration />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
{!!timeFieldName && getTimeFieldMessage()}
|
||||
{isTimeBased && getTimeFieldMessage()}
|
||||
{(hasFilters || hasQuery) && (
|
||||
<AdjustSearch
|
||||
hasFilters={hasFilters}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { getTopNavLinks } from './get_top_nav_links';
|
|||
import { Query, TimeRange } from '../../../../../../data/common/query';
|
||||
import { getHeaderActionMenuMounter } from '../../../../kibana_services';
|
||||
import { GetStateReturn } from '../../services/discover_state';
|
||||
import { DataViewType } from '../../../../../../data_views/common';
|
||||
|
||||
export type DiscoverTopNavProps = Pick<
|
||||
DiscoverLayoutProps,
|
||||
|
@ -39,7 +40,10 @@ export const DiscoverTopNav = ({
|
|||
resetSavedSearch,
|
||||
}: DiscoverTopNavProps) => {
|
||||
const history = useHistory();
|
||||
const showDatePicker = useMemo(() => indexPattern.isTimeBased(), [indexPattern]);
|
||||
const showDatePicker = useMemo(
|
||||
() => indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP,
|
||||
[indexPattern]
|
||||
);
|
||||
const { TopNavMenu } = services.navigation.ui;
|
||||
|
||||
const onOpenSavedSearch = useCallback(
|
||||
|
|
|
@ -26,6 +26,7 @@ import { DataPublicPluginStart } from '../../../../../data/public';
|
|||
import { SavedSearchData } from './use_saved_search';
|
||||
import { DiscoverServices } from '../../../build_services';
|
||||
import { ReduxLikeStateContainer } from '../../../../../kibana_utils/common';
|
||||
import { DataViewType } from '../../../../../data_views/common';
|
||||
|
||||
export function fetchAll(
|
||||
dataSubjects: SavedSearchData,
|
||||
|
@ -72,16 +73,17 @@ export function fetchAll(
|
|||
},
|
||||
};
|
||||
|
||||
const isChartVisible =
|
||||
!hideChart && indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP;
|
||||
|
||||
const all = forkJoin({
|
||||
documents: fetchDocuments(dataSubjects, searchSource.createCopy(), subFetchDeps),
|
||||
totalHits:
|
||||
hideChart || !indexPattern.timeFieldName
|
||||
? fetchTotalHits(dataSubjects, searchSource.createCopy(), subFetchDeps)
|
||||
: of(null),
|
||||
chart:
|
||||
!hideChart && indexPattern.timeFieldName
|
||||
? fetchChart(dataSubjects, searchSource.createCopy(), subFetchDeps)
|
||||
: of(null),
|
||||
totalHits: !isChartVisible
|
||||
? fetchTotalHits(dataSubjects, searchSource.createCopy(), subFetchDeps)
|
||||
: of(null),
|
||||
chart: isChartVisible
|
||||
? fetchChart(dataSubjects, searchSource.createCopy(), subFetchDeps)
|
||||
: of(null),
|
||||
});
|
||||
|
||||
all.subscribe(
|
||||
|
|
|
@ -38,6 +38,13 @@ export const fetchDocuments = (
|
|||
searchSource.setField('trackTotalHits', false);
|
||||
searchSource.setField('highlightAll', true);
|
||||
searchSource.setField('version', true);
|
||||
if (searchSource.getField('index')?.type === 'rollup') {
|
||||
// We treat that index pattern as "normal" even if it was a rollup index pattern,
|
||||
// since the rollup endpoint does not support querying individual documents, but we
|
||||
// can get them from the regular _search API that will be used if the index pattern
|
||||
// not a rollup index pattern.
|
||||
searchSource.setOverwriteDataViewType(undefined);
|
||||
}
|
||||
|
||||
sendLoadingMsg(documents$);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
isCompleteResponse,
|
||||
ISearchSource,
|
||||
} from '../../../../../data/public';
|
||||
import { DataViewType } from '../../../../../data_views/common';
|
||||
import { Adapters } from '../../../../../inspector/common';
|
||||
import { FetchStatus } from '../../types';
|
||||
import { SavedSearchData } from './use_saved_search';
|
||||
|
@ -36,13 +37,18 @@ export function fetchTotalHits(
|
|||
}
|
||||
) {
|
||||
const { totalHits$ } = data$;
|
||||
const indexPattern = searchSource.getField('index');
|
||||
searchSource.setField('trackTotalHits', true);
|
||||
searchSource.setField('filter', data.query.timefilter.timefilter.createFilter(indexPattern!));
|
||||
searchSource.setField('size', 0);
|
||||
searchSource.removeField('sort');
|
||||
searchSource.removeField('fields');
|
||||
searchSource.removeField('aggs');
|
||||
if (searchSource.getField('index')?.type === DataViewType.ROLLUP) {
|
||||
// We treat that index pattern as "normal" even if it was a rollup index pattern,
|
||||
// since the rollup endpoint does not support querying individual documents, but we
|
||||
// can get them from the regular _search API that will be used if the index pattern
|
||||
// not a rollup index pattern.
|
||||
searchSource.setOverwriteDataViewType(undefined);
|
||||
}
|
||||
|
||||
sendLoadingMsg(totalHits$);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { SORT_DEFAULT_ORDER_SETTING } from '../../../../common';
|
||||
import { IndexPattern, ISearchSource } from '../../../../../data/common';
|
||||
import { DataViewType } from '../../../../../data_views/common';
|
||||
import type { SortOrder } from '../../../services/saved_searches';
|
||||
import { DiscoverServices } from '../../../build_services';
|
||||
import { getSortForSearchSource } from '../../../components/doc_table';
|
||||
|
@ -44,14 +45,9 @@ export function updateSearchSource(
|
|||
indexPattern,
|
||||
uiSettings.get(SORT_DEFAULT_ORDER_SETTING)
|
||||
);
|
||||
searchSource
|
||||
.setField('trackTotalHits', true)
|
||||
.setField('sort', usedSort)
|
||||
// Even when searching rollups, we want to use the default strategy so that we get back a
|
||||
// document-like response.
|
||||
.setPreferredSearchStrategyId('default');
|
||||
searchSource.setField('trackTotalHits', true).setField('sort', usedSort);
|
||||
|
||||
if (indexPattern.type !== 'rollup') {
|
||||
if (indexPattern.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
|
||||
searchSource.setField('filter', data.query.timefilter.timefilter.createFilter(indexPattern));
|
||||
}
|
||||
|
|
|
@ -124,8 +124,8 @@ describe('getSavedSearch', () => {
|
|||
"serialize": [MockFunction],
|
||||
"setField": [MockFunction],
|
||||
"setFields": [MockFunction],
|
||||
"setOverwriteDataViewType": [MockFunction],
|
||||
"setParent": [MockFunction],
|
||||
"setPreferredSearchStrategyId": [MockFunction],
|
||||
},
|
||||
"sharingSavedObjectProps": Object {
|
||||
"aliasTargetId": undefined,
|
||||
|
|
|
@ -68,9 +68,10 @@ describe('saved_searches_utils', () => {
|
|||
"history": Array [],
|
||||
"id": "data_source1",
|
||||
"inheritOptions": Object {},
|
||||
"overwriteDataViewType": undefined,
|
||||
"parent": undefined,
|
||||
"requestStartHandlers": Array [],
|
||||
"searchStrategyId": undefined,
|
||||
"shouldOverwriteDataViewType": false,
|
||||
},
|
||||
"sharingSavedObjectProps": Object {},
|
||||
"sort": Array [],
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"source": {
|
||||
"index-pattern": {
|
||||
"fields":"[]",
|
||||
"timeFieldName": "@timestamp",
|
||||
"timeFieldName": "nested.timestamp",
|
||||
"title": "date-nested"
|
||||
},
|
||||
"type": "index-pattern"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue