[UnifiedSearch] Add explore matching indices to data view menu (#141807)

* add explore matching index

* adjust type

* move things around

* fix types

* fix tests

* fix imports

* fix limit

* do not clean datasource on adding ad hoc data view

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
Joe Reuter 2022-09-30 11:37:41 +02:00 committed by GitHub
parent 2d8eb0eed6
commit b885d9381d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 308 additions and 137 deletions

View file

@ -23,7 +23,7 @@ pageLoadAssetSize:
dataViewEditor: 12000
dataViewFieldEditor: 27000
dataViewManagement: 5000
dataViews: 44532
dataViews: 46532
dataVisualizer: 27530
devTools: 38637
discover: 99999

View file

@ -10,7 +10,12 @@ import React, { useState, useEffect, useCallback, useRef } from 'react';
import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import memoizeOne from 'memoize-one';
import { DataViewField } from '@kbn/data-views-plugin/public';
import {
DataViewField,
DataViewsPublicPluginStart,
INDEX_PATTERN_TYPE,
MatchedItem,
} from '@kbn/data-views-plugin/public';
import {
DataView,
@ -23,16 +28,14 @@ import {
UseField,
} from '../shared_imports';
import { ensureMinimumTime, getIndices, extractTimeFields, getMatchedIndices } from '../lib';
import { ensureMinimumTime, extractTimeFields, getMatchedIndices } from '../lib';
import { FlyoutPanels } from './flyout_panels';
import { removeSpaces } from '../lib';
import {
MatchedItem,
DataViewEditorContext,
RollupIndicesCapsResponse,
INDEX_PATTERN_TYPE,
IndexPatternConfig,
MatchedIndicesSet,
FormInternal,
@ -176,18 +179,19 @@ const IndexPatternEditorFlyoutContentComponent = ({
// load all data sources and set initial matchedIndices
const loadSources = useCallback(() => {
getIndices({
http,
isRollupIndex: () => false,
pattern: '*',
showAllIndices: allowHidden,
}).then((dataSources) => {
setAllSources(dataSources);
const matchedSet = getMatchedIndices(dataSources, [], [], allowHidden);
setMatchedIndices(matchedSet);
setIsLoadingSources(false);
});
}, [http, allowHidden]);
dataViews
.getIndices({
isRollupIndex: () => false,
pattern: '*',
showAllIndices: allowHidden,
})
.then((dataSources) => {
setAllSources(dataSources);
const matchedSet = getMatchedIndices(dataSources, [], [], allowHidden);
setMatchedIndices(matchedSet);
setIsLoadingSources(false);
});
}, [allowHidden, dataViews]);
// loading list of index patterns
useEffect(() => {
@ -271,7 +275,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
const { matchedIndicesResult, exactMatched } = !isLoadingSources
? await loadMatchedIndices(query, allowHidden, allSources, {
isRollupIndex,
http,
dataViews,
})
: {
matchedIndicesResult: {
@ -302,7 +306,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
return fetchIndices(newTitle);
},
[http, allowHidden, allSources, type, rollupIndicesCapabilities, isLoadingSources]
[dataViews, allowHidden, allSources, type, rollupIndicesCapabilities, isLoadingSources]
);
// If editData exists, loadSources so that MatchedIndices can be loaded for the Timestampfields
@ -453,10 +457,10 @@ const loadMatchedIndices = memoizeOne(
allSources: MatchedItem[],
{
isRollupIndex,
http,
dataViews,
}: {
isRollupIndex: (index: string) => boolean;
http: DataViewEditorContext['http'];
dataViews: DataViewsPublicPluginStart;
}
): Promise<{
matchedIndicesResult: MatchedIndicesSet;
@ -466,8 +470,7 @@ const loadMatchedIndices = memoizeOne(
const indexRequests = [];
if (query?.endsWith('*')) {
const exactMatchedQuery = getIndices({
http,
const exactMatchedQuery = dataViews.getIndices({
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
@ -476,14 +479,12 @@ const loadMatchedIndices = memoizeOne(
// provide default value when not making a request for the partialMatchQuery
indexRequests.push(Promise.resolve([]));
} else {
const exactMatchQuery = getIndices({
http,
const exactMatchQuery = dataViews.getIndices({
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
});
const partialMatchQuery = getIndices({
http,
const partialMatchQuery = dataViews.getIndices({
isRollupIndex,
pattern: `${query}*`,
showAllIndices: allowHidden,

View file

@ -9,6 +9,7 @@
import React, { ChangeEvent, useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
import { MatchedItem } from '@kbn/data-views-plugin/public';
import {
UseField,
getFieldValidityAndErrorMessage,
@ -17,12 +18,7 @@ import {
} from '../../shared_imports';
import { canAppendWildcard, removeSpaces } from '../../lib';
import { schema } from '../form_schema';
import {
MatchedItem,
RollupIndicesCapsResponse,
IndexPatternConfig,
MatchedIndicesSet,
} from '../../types';
import { RollupIndicesCapsResponse, IndexPatternConfig, MatchedIndicesSet } from '../../types';
interface RefreshMatchedIndicesResult {
matchedIndicesResult: MatchedIndicesSet;

View file

@ -20,9 +20,10 @@ import {
EuiBadge,
} from '@elastic/eui';
import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public';
import { UseField } from '../../shared_imports';
import { INDEX_PATTERN_TYPE, IndexPatternConfig } from '../../types';
import { IndexPatternConfig } from '../../types';
interface TypeFieldProps {
onChange: (type: INDEX_PATTERN_TYPE) => void;

View file

@ -6,9 +6,9 @@
* Side Public License, v 1.
*/
import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n';
import { fieldValidators, ValidationFunc } from '../shared_imports';
import { INDEX_PATTERN_TYPE } from '../types';
export const singleAstriskValidator = (
...args: Parameters<ValidationFunc>

View file

@ -9,7 +9,7 @@
import React from 'react';
import { IndicesList } from '.';
import { shallow } from 'enzyme';
import { MatchedItem } from '../../../types';
import { MatchedItem } from '@kbn/data-views-plugin/public';
const indices = [
{ name: 'kibana', tags: [] },

View file

@ -27,7 +27,7 @@ import {
import { Pager } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { MatchedItem, Tag } from '../../../types';
import { MatchedItem, Tag } from '@kbn/data-views-plugin/public';
interface IndicesListProps {
indices: MatchedItem[];

View file

@ -8,10 +8,11 @@
import React from 'react';
import { EuiSpacer } from '@elastic/eui';
import { INDEX_PATTERN_TYPE } from '@kbn/data-views-plugin/public';
import { StatusMessage } from './status_message';
import { IndicesList } from './indices_list';
import { INDEX_PATTERN_TYPE, MatchedIndicesSet } from '../../types';
import { MatchedIndicesSet } from '../../types';
interface Props {
type: INDEX_PATTERN_TYPE;

View file

@ -9,7 +9,7 @@
import React from 'react';
import { StatusMessage } from '.';
import { shallow } from 'enzyme';
import { MatchedItem } from '../../../types';
import { MatchedItem } from '@kbn/data-views-plugin/public';
const tagsPartial = {
tags: [],

View file

@ -6,8 +6,9 @@
* Side Public License, v 1.
*/
import { MatchedItem } from '@kbn/data-views-plugin/public';
import { Tag } from '@kbn/data-views-plugin/public/types';
import { getMatchedIndices } from './get_matched_indices';
import { Tag, MatchedItem } from '../types';
jest.mock('../constants', () => ({
MAX_NUMBER_OF_MATCHING_INDICES: 6,

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import { MatchedItem } from '@kbn/data-views-plugin/public';
import { MAX_NUMBER_OF_MATCHING_INDICES } from '../constants';
function isSystemIndex(index: string): boolean {
@ -50,7 +51,7 @@ function filterSystemIndices(indices: MatchedItem[], isIncludingSystemIndices: b
We call this `exact` matches because ES is telling us exactly what it matches
*/
import { MatchedItem, MatchedIndicesSet } from '../types';
import { MatchedIndicesSet } from '../types';
export function getMatchedIndices(
unfilteredAllIndices: MatchedItem[],

View file

@ -10,8 +10,6 @@ export { canAppendWildcard } from './can_append_wildcard';
export { ensureMinimumTime } from './ensure_minimum_time';
export { getIndices } from './get_indices';
export { getMatchedIndices } from './get_matched_indices';
export { containsIllegalCharacters } from './contains_illegal_characters';

View file

@ -17,7 +17,12 @@ import {
import { EuiComboBoxOptionOption } from '@elastic/eui';
import type { DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type {
DataView,
DataViewsPublicPluginStart,
INDEX_PATTERN_TYPE,
MatchedItem,
} from '@kbn/data-views-plugin/public';
import { DataPublicPluginStart, IndexPatternAggRestrictions } from './shared_imports';
export interface DataViewEditorContext {
@ -80,51 +85,6 @@ export interface StartPlugins {
export type CloseEditor = () => void;
export interface MatchedItem {
name: string;
tags: Tag[];
item: {
name: string;
backing_indices?: string[];
timestamp_field?: string;
indices?: string[];
aliases?: string[];
attributes?: ResolveIndexResponseItemIndexAttrs[];
data_stream?: string;
};
}
// for showing index matches
export interface ResolveIndexResponse {
indices?: ResolveIndexResponseItemIndex[];
aliases?: ResolveIndexResponseItemAlias[];
data_streams?: ResolveIndexResponseItemDataStream[];
}
export interface ResolveIndexResponseItem {
name: string;
}
export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem {
backing_indices: string[];
timestamp_field: string;
}
export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem {
indices: string[];
}
export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem {
aliases?: string[];
attributes?: ResolveIndexResponseItemIndexAttrs[];
data_stream?: string;
}
export interface Tag {
name: string;
key: string;
color: string;
}
// end for index matches
export interface IndexPatternTableItem {
@ -135,13 +95,6 @@ export interface IndexPatternTableItem {
sort: string;
}
export enum ResolveIndexResponseItemIndexAttrs {
OPEN = 'open',
CLOSED = 'closed',
HIDDEN = 'hidden',
FROZEN = 'frozen',
}
export interface RollupIndiciesCapability {
aggs: Record<string, IndexPatternAggRestrictions>;
error: string;
@ -149,11 +102,6 @@ export interface RollupIndiciesCapability {
export type RollupIndicesCapsResponse = Record<string, RollupIndiciesCapability>;
export enum INDEX_PATTERN_TYPE {
ROLLUP = 'rollup',
DEFAULT = 'default',
}
export interface IndexPatternConfig {
title: string;
timestampField?: EuiComboBoxOptionOption<string>;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { DataViewsService } from '.';
import { DataViewsService, MatchedItem } from '.';
import { DataViewsServiceDeps } from '../common/data_views/data_views';
import { HasDataService } from '../common';
@ -24,6 +24,11 @@ export interface DataViewsServicePublicDeps extends DataViewsServiceDeps {
* Has data service
*/
hasData: HasDataService;
getIndices: (props: {
pattern: string;
showAllIndices?: boolean;
isRollupIndex: (indexName: string) => boolean;
}) => Promise<MatchedItem[]>;
}
/**
@ -32,6 +37,12 @@ export interface DataViewsServicePublicDeps extends DataViewsServiceDeps {
*/
export class DataViewsServicePublic extends DataViewsService {
public getCanSaveSync: () => boolean;
public getIndices: (props: {
pattern: string;
showAllIndices?: boolean;
isRollupIndex: (indexName: string) => boolean;
}) => Promise<MatchedItem[]>;
public hasData: HasDataService;
/**
@ -43,5 +54,6 @@ export class DataViewsServicePublic extends DataViewsService {
super(deps);
this.getCanSaveSync = deps.getCanSaveSync;
this.hasData = deps.hasData;
this.getIndices = deps.getIndices;
}
}

View file

@ -32,7 +32,14 @@ export {
getFieldSubtypeNested,
} from '../common';
export type { DataViewsPublicSetupDependencies, DataViewsPublicStartDependencies } from './types';
export type {
DataViewsPublicSetupDependencies,
DataViewsPublicStartDependencies,
MatchedItem,
Tag,
} from './types';
export { INDEX_PATTERN_TYPE } from './types';
export type {
DataViewsServicePublic,

View file

@ -21,7 +21,7 @@ import { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper
import { UiSettingsPublicToCommon } from './ui_settings_wrapper';
import { DataViewsServicePublic } from './data_views_service_public';
import { HasData } from './services';
import { getIndices, HasData } from './services';
import { debounceByKey } from './debounce_by_key';
@ -76,6 +76,7 @@ export class DataViewsPublicPlugin
getCanSaveSync: () => application.capabilities.indexPatterns.save === true,
getCanSaveAdvancedSettings: () =>
Promise.resolve(application.capabilities.advancedSettings.save === true),
getIndices: (props) => getIndices({ ...props, http: core.http }),
});
}

View file

@ -12,20 +12,20 @@ import { i18n } from '@kbn/i18n';
import { Tag, INDEX_PATTERN_TYPE } from '../types';
import { MatchedItem, ResolveIndexResponse, ResolveIndexResponseItemIndexAttrs } from '../types';
const aliasLabel = i18n.translate('indexPatternEditor.aliasLabel', { defaultMessage: 'Alias' });
const dataStreamLabel = i18n.translate('indexPatternEditor.dataStreamLabel', {
const aliasLabel = i18n.translate('dataViews.aliasLabel', { defaultMessage: 'Alias' });
const dataStreamLabel = i18n.translate('dataViews.dataStreamLabel', {
defaultMessage: 'Data stream',
});
const indexLabel = i18n.translate('indexPatternEditor.indexLabel', {
const indexLabel = i18n.translate('dataViews.indexLabel', {
defaultMessage: 'Index',
});
const frozenLabel = i18n.translate('indexPatternEditor.frozenLabel', {
const frozenLabel = i18n.translate('dataViews.frozenLabel', {
defaultMessage: 'Frozen',
});
const rollupLabel = i18n.translate('indexPatternEditor.rollupLabel', {
const rollupLabel = i18n.translate('dataViews.rollupLabel', {
defaultMessage: 'Rollup',
});

View file

@ -6,4 +6,17 @@
* Side Public License, v 1.
*/
import { HttpStart } from '@kbn/core/public';
import { MatchedItem } from '../types';
export * from './has_data';
export async function getIndices(props: {
http: HttpStart;
pattern: string;
showAllIndices?: boolean;
isRollupIndex: (indexName: string) => boolean;
}): Promise<MatchedItem[]> {
const { getIndices: getIndicesLazy } = await import('./get_indices');
return getIndicesLazy(props);
}

View file

@ -11,6 +11,11 @@ import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/
import { DataViewsServicePublicMethods } from './data_views';
import { HasDataService } from '../common';
export enum INDEX_PATTERN_TYPE {
ROLLUP = 'rollup',
DEFAULT = 'default',
}
export enum IndicesResponseItemIndexAttrs {
OPEN = 'open',
CLOSED = 'closed',
@ -98,6 +103,11 @@ export interface DataViewsPublicPluginSetup {}
export interface DataViewsServicePublic extends DataViewsServicePublicMethods {
getCanSaveSync: () => boolean;
hasData: HasDataService;
getIndices: (props: {
pattern: string;
showAllIndices?: boolean;
isRollupIndex: (indexName: string) => boolean;
}) => Promise<MatchedItem[]>;
}
export type DataViewsContract = DataViewsServicePublic;
@ -106,3 +116,55 @@ export type DataViewsContract = DataViewsServicePublic;
* Data views plugin public Start contract
*/
export type DataViewsPublicPluginStart = DataViewsServicePublic;
export interface MatchedItem {
name: string;
tags: Tag[];
item: {
name: string;
backing_indices?: string[];
timestamp_field?: string;
indices?: string[];
aliases?: string[];
attributes?: ResolveIndexResponseItemIndexAttrs[];
data_stream?: string;
};
}
// for showing index matches
export interface ResolveIndexResponse {
indices?: ResolveIndexResponseItemIndex[];
aliases?: ResolveIndexResponseItemAlias[];
data_streams?: ResolveIndexResponseItemDataStream[];
}
export interface ResolveIndexResponseItem {
name: string;
}
export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem {
backing_indices: string[];
timestamp_field: string;
}
export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem {
indices: string[];
}
export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem {
aliases?: string[];
attributes?: ResolveIndexResponseItemIndexAttrs[];
data_stream?: string;
}
export interface Tag {
name: string;
key: string;
color: string;
}
export enum ResolveIndexResponseItemIndexAttrs {
OPEN = 'open',
CLOSED = 'closed',
HIDDEN = 'hidden',
FROZEN = 'frozen',
}

View file

@ -66,7 +66,7 @@ export const DiscoverTopNav = ({
[dataView]
);
const services = useDiscoverServices();
const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings } = services;
const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings, dataViews } = services;
const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView());
@ -141,6 +141,19 @@ export const DiscoverTopNav = ({
[canEditDataView, dataViewEditor, onChangeDataView]
);
const onCreateDefaultAdHocDataView = useCallback(
async (pattern: string) => {
const newDataView = await dataViews.create({
title: pattern,
});
if (newDataView.fields.getByName('@timestamp')?.type === 'date') {
newDataView.timeFieldName = '@timestamp';
}
onChangeDataView(newDataView.id!);
},
[dataViews, onChangeDataView]
);
const topNavMenu = useMemo(
() =>
getTopNavLinks({
@ -201,6 +214,7 @@ export const DiscoverTopNav = ({
currentDataViewId: dataView?.id,
onAddField: addField,
onDataViewCreated: createNewDataView,
onCreateDefaultAdHocDataView,
onChangeDataView,
textBasedLanguages: supportedTextBasedLanguages as DataViewPickerProps['textBasedLanguages'],
adHocDataViews: adHocDataViewList,

View file

@ -7,7 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { css } from '@emotion/react';
import {
EuiPopover,
@ -24,6 +24,7 @@ import {
EuiFlexItem,
EuiButtonEmpty,
EuiToolTip,
EuiSpacer,
} from '@elastic/eui';
import type { DataViewListItem } from '@kbn/data-views-plugin/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
@ -71,9 +72,13 @@ export function ChangeDataView({
onTextLangQuerySubmit,
textBasedLanguage,
isDisabled,
onCreateDefaultAdHocDataView,
}: DataViewPickerPropsExtended) {
const { euiTheme } = useEuiTheme();
const [isPopoverOpen, setPopoverIsOpen] = useState(false);
const [noDataViewMatches, setNoDataViewMatches] = useState(false);
const [dataViewSearchString, setDataViewSearchString] = useState('');
const [indexMatches, setIndexMatches] = useState(0);
const [dataViewsList, setDataViewsList] = useState<DataViewListItem[]>([]);
const [triggerLabel, setTriggerLabel] = useState('');
const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState(
@ -111,6 +116,24 @@ export function ChangeDataView({
fetchDataViews();
}, [data, currentDataViewId, adHocDataViews]);
const pendingIndexMatch = useRef<undefined | NodeJS.Timeout>();
useEffect(() => {
async function checkIndices() {
if (dataViewSearchString !== '' && noDataViewMatches) {
const matches = await kibana.services.dataViews.getIndices({
pattern: dataViewSearchString,
isRollupIndex: () => false,
showAllIndices: false,
});
setIndexMatches(matches.length);
}
}
if (pendingIndexMatch.current) {
clearTimeout(pendingIndexMatch.current);
}
pendingIndexMatch.current = setTimeout(checkIndices, 250);
}, [dataViewSearchString, kibana.services.dataViews, noDataViewMatches]);
useEffect(() => {
if (trigger.label) {
if (textBasedLanguage) {
@ -282,10 +305,57 @@ export function ChangeDataView({
}
}}
currentDataViewId={currentDataViewId}
selectableProps={selectableProps}
selectableProps={{
...(selectableProps || {}),
// @ts-expect-error Some EUI weirdness
searchProps: {
...(selectableProps?.searchProps || {}),
onChange: (value, matches) => {
selectableProps?.searchProps?.onChange?.(value, matches);
setNoDataViewMatches(matches.length === 0 && dataViewsList.length > 0);
setDataViewSearchString(value);
},
},
}}
searchListInputId={searchListInputId}
isTextBasedLangSelected={isTextBasedLangSelected}
/>
{onCreateDefaultAdHocDataView && noDataViewMatches && indexMatches > 0 && (
<EuiFlexGroup
alignItems="center"
gutterSize="none"
justifyContent="spaceBetween"
data-test-subj="select-text-based-language-panel"
css={css`
margin: ${euiTheme.size.s};
margin-bottom: 0;
`}
>
<EuiFlexItem grow={true}>
<EuiButton
fullWidth
size="s"
onClick={() => {
setPopoverIsOpen(false);
onCreateDefaultAdHocDataView(dataViewSearchString);
}}
>
{i18n.translate(
'unifiedSearch.query.queryBar.indexPattern.createForMatchingIndices',
{
defaultMessage: `Explore {indicesLength, plural,
one {# matching index}
other {# matching indices}}`,
values: {
indicesLength: indexMatches,
},
}
)}
</EuiButton>
<EuiSpacer size="s" />
</EuiFlexItem>
</EuiFlexGroup>
)}
</>
);

View file

@ -63,6 +63,8 @@ export interface DataViewPickerProps {
* Also works as a flag to show the create dataview button.
*/
onDataViewCreated?: () => void;
onCreateDefaultAdHocDataView?: (pattern: string) => void;
/**
* List of the supported text based languages (SQL, ESQL) etc.
* Defined per application, if not provided, no text based languages
@ -104,6 +106,7 @@ export const DataViewPicker = ({
onSaveTextLanguageQuery,
onTextLangQuerySubmit,
textBasedLanguage,
onCreateDefaultAdHocDataView,
isDisabled,
}: DataViewPickerPropsExtended) => {
return (
@ -113,6 +116,7 @@ export const DataViewPicker = ({
onChangeDataView={onChangeDataView}
onAddField={onAddField}
onDataViewCreated={onDataViewCreated}
onCreateDefaultAdHocDataView={onCreateDefaultAdHocDataView}
trigger={trigger}
adHocDataViews={adHocDataViews}
selectableProps={selectableProps}

View file

@ -86,5 +86,6 @@ export interface IUnifiedSearchPluginServices extends Partial<CoreStart> {
storage: IStorageWrapper;
docLinks: DocLinksStart;
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
usageCollection?: UsageCollectionStart;
}

View file

@ -17,6 +17,7 @@
{ "path": "../../core/tsconfig.json" },
{ "path": "../data/tsconfig.json" },
{ "path": "../data_views/tsconfig.json" },
{ "path": "../data_view_editor/tsconfig.json" },
{ "path": "../embeddable/tsconfig.json" },
{ "path": "../usage_collection/tsconfig.json" },
{ "path": "../kibana_utils/tsconfig.json" },

View file

@ -15,8 +15,8 @@ import { downloadMultipleAs } from '@kbn/share-plugin/public';
import { tableHasFormulas } from '@kbn/data-plugin/common';
import { exporters, getEsQueryConfig } from '@kbn/data-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { DataViewPickerProps } from '@kbn/unified-search-plugin/public';
import { ENABLE_SQL } from '../../common';
import {
LensAppServices,
@ -768,13 +768,15 @@ export const LensTopNavMenu = ({
closeDataViewEditor.current = dataViewEditor.openEditor({
onSave: async (dataView) => {
if (dataView.id) {
dispatch(
switchAndCleanDatasource({
newDatasourceId: 'indexpattern',
visualizationId: visualization?.activeId,
currentIndexPatternId: dataView?.id,
})
);
if (isOnTextBasedMode) {
dispatch(
switchAndCleanDatasource({
newDatasourceId: 'indexpattern',
visualizationId: visualization?.activeId,
currentIndexPatternId: dataView?.id,
})
);
}
dispatchChangeIndexPattern(dataView);
setCurrentIndexPattern(dataView);
}
@ -783,7 +785,43 @@ export const LensTopNavMenu = ({
});
}
: undefined,
[canEditDataView, dataViewEditor, dispatch, dispatchChangeIndexPattern, visualization?.activeId]
[
canEditDataView,
dataViewEditor,
dispatch,
dispatchChangeIndexPattern,
isOnTextBasedMode,
visualization?.activeId,
]
);
const onCreateDefaultAdHocDataView = useCallback(
async (pattern: string) => {
const dataView = await dataViewsService.create({
title: pattern,
});
if (dataView.fields.getByName('@timestamp')?.type === 'date') {
dataView.timeFieldName = '@timestamp';
}
if (isOnTextBasedMode) {
dispatch(
switchAndCleanDatasource({
newDatasourceId: 'indexpattern',
visualizationId: visualization?.activeId,
currentIndexPatternId: dataView?.id,
})
);
}
dispatchChangeIndexPattern(dataView);
setCurrentIndexPattern(dataView);
},
[
dataViewsService,
dispatch,
dispatchChangeIndexPattern,
isOnTextBasedMode,
visualization?.activeId,
]
);
// setting that enables/disables SQL
@ -793,7 +831,7 @@ export const LensTopNavMenu = ({
supportedTextBasedLanguages.push('SQL');
}
const dataViewPickerProps = {
const dataViewPickerProps: DataViewPickerProps = {
trigger: {
label: currentIndexPattern?.getName?.() || '',
'data-test-subj': 'lns-dataView-switch-link',
@ -802,6 +840,7 @@ export const LensTopNavMenu = ({
currentDataViewId: currentIndexPattern?.id,
onAddField: addField,
onDataViewCreated: createNewDataView,
onCreateDefaultAdHocDataView,
adHocDataViews: indexPatterns.filter((pattern) => !pattern.isPersisted()),
onChangeDataView: (newIndexPatternId: string) => {
const currentDataView = dataViewsList.find(

View file

@ -3720,10 +3720,10 @@
"indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "Votre modèle d'indexation ne correspond à aucun flux de données, index ni alias d'index, mais {strongIndices} {matchedIndicesLength, plural, one {est semblable} other {sont semblables} }.",
"indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, one {source} other {# sources} }",
"indexPatternEditor.status.successLabel.successDetail": "Votre modèle d'indexation correspond à {sourceCount} {sourceCount, plural, one {source} other {sources} }.",
"indexPatternEditor.aliasLabel": "Alias",
"dataViews.aliasLabel": "Alias",
"indexPatternEditor.createIndex.noMatch": "Le nom doit correspondre à au moins un flux de données, index ou alias d'index.",
"indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel": "--- Je ne souhaite pas utiliser le filtre temporel ---",
"indexPatternEditor.dataStreamLabel": "Flux de données",
"dataViews.dataStreamLabel": "Flux de données",
"indexPatternEditor.dataView.unableSaveLabel": "Échec de l'enregistrement de la vue de données.",
"indexPatternEditor.dataViewExists.ValidationErrorMessage": "Une vue de données de ce nom existe déjà.",
"indexPatternEditor.editDataView.editConfirmationModal.confirmButton": "Confirmer",
@ -3754,8 +3754,8 @@
"indexPatternEditor.form.customIndexPatternIdLabel": "ID de vue de données personnalisé",
"indexPatternEditor.form.nameAriaLabel": "Champ de nom facultatif",
"indexPatternEditor.form.titleAriaLabel": "Champ de modèle d'indexation",
"indexPatternEditor.frozenLabel": "Frozen",
"indexPatternEditor.indexLabel": "Index",
"dataViews.frozenLabel": "Frozen",
"dataViews.indexLabel": "Index",
"indexPatternEditor.loadingHeader": "Recherche d'index correspondants…",
"indexPatternEditor.requireTimestampOption.ValidationErrorMessage": "Sélectionnez un champ d'horodatage.",
"indexPatternEditor.rollupDataView.createIndex.noMatchError": "Erreur de vue de données de cumul : doit correspondre à un index de cumul",
@ -3763,7 +3763,7 @@
"indexPatternEditor.rollupDataView.warning.textParagraphOne": "Kibana propose un support bêta pour les vues de données basées sur les cumuls. Vous pourriez rencontrer des problèmes lors de l'utilisation de ces vues dans les recherches enregistrées, les visualisations et les tableaux de bord. Ils ne sont pas compatibles avec certaines fonctionnalités avancées, telles que Timelion et le Machine Learning.",
"indexPatternEditor.rollupDataView.warning.textParagraphTwo": "Vous pouvez mettre une vue de données de cumul en correspondance avec un index de cumul et zéro index régulier ou plus. Une vue de données de cumul dispose d'indicateurs, de champs, d'intervalles et d'agrégations limités. Un index de cumul se limite aux index disposant d'une configuration de tâche ou de plusieurs tâches avec des configurations compatibles.",
"indexPatternEditor.rollupIndexPattern.warning.title": "Fonctionnalité bêta",
"indexPatternEditor.rollupLabel": "Cumul",
"dataViews.rollupLabel": "Cumul",
"indexPatternEditor.saved": "'{indexPatternName}' enregistré",
"indexPatternEditor.status.noSystemIndicesLabel": "Aucun flux de données, index ni alias d'index ne correspond à votre modèle d'indexation.",
"indexPatternEditor.status.noSystemIndicesWithPromptLabel": "Aucun flux de données, index ni alias d'index ne correspond à votre modèle d'indexation.",

View file

@ -3715,10 +3715,10 @@
"indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "インデックスパターンはどのデータストリーム、インデックス、インデックスエイリアスとも一致しませんが、{strongIndices} {matchedIndicesLength, plural, other {が} }類似しています。",
"indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, other {# ソース} }",
"indexPatternEditor.status.successLabel.successDetail": "インデックスパターンは、{sourceCount} {sourceCount, plural, other {ソース} }と一致します。",
"indexPatternEditor.aliasLabel": "エイリアス",
"dataViews.aliasLabel": "エイリアス",
"indexPatternEditor.createIndex.noMatch": "名前は1つ以上のデータストリーム、インデックス、またはインデックスエイリアスと一致する必要があります。",
"indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel": "--- 時間フィルターを使用しない ---",
"indexPatternEditor.dataStreamLabel": "データストリーム",
"dataViews.dataStreamLabel": "データストリーム",
"indexPatternEditor.dataView.unableSaveLabel": "データビューの保存に失敗しました。",
"indexPatternEditor.dataViewExists.ValidationErrorMessage": "この名前のデータビューはすでに存在します。",
"indexPatternEditor.editDataView.editConfirmationModal.confirmButton": "確認",
@ -3749,8 +3749,8 @@
"indexPatternEditor.form.customIndexPatternIdLabel": "カスタムデータビューID",
"indexPatternEditor.form.nameAriaLabel": "名前フィールド(任意)",
"indexPatternEditor.form.titleAriaLabel": "インデックスパターンフィールド",
"indexPatternEditor.frozenLabel": "凍結",
"indexPatternEditor.indexLabel": "インデックス",
"dataViews.frozenLabel": "凍結",
"dataViews.indexLabel": "インデックス",
"indexPatternEditor.loadingHeader": "一致するインデックスを検索中…",
"indexPatternEditor.requireTimestampOption.ValidationErrorMessage": "タイムスタンプフィールドを選択します。",
"indexPatternEditor.rollupDataView.createIndex.noMatchError": "ロールアップデータビューエラー:ロールアップインデックスの 1 つと一致している必要があります",
@ -3758,7 +3758,7 @@
"indexPatternEditor.rollupDataView.warning.textParagraphOne": "Kibanaではロールアップに基づいてデータビューのデータサポートを提供します。保存された検索、可視化、ダッシュボードでこれらを使用すると問題が発生する場合があります。Timelionや機械学習などの一部の高度な機能ではサポートされていません。",
"indexPatternEditor.rollupDataView.warning.textParagraphTwo": "ロールアップデータビューは、1つのロールアップインデックスとゼロ以上の標準インデックスと一致させることができます。ロールアップデータビューでは、メトリック、フィールド、間隔、アグリゲーションが制限されています。ロールアップインデックスは、1つのジョブ構成があるインデックス、または複数のジョブと互換する構成があるインデックスに制限されています。",
"indexPatternEditor.rollupIndexPattern.warning.title": "ベータ機能",
"indexPatternEditor.rollupLabel": "ロールアップ",
"dataViews.rollupLabel": "ロールアップ",
"indexPatternEditor.saved": "'{indexPatternName}'が保存されました",
"indexPatternEditor.status.noSystemIndicesLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。",
"indexPatternEditor.status.noSystemIndicesWithPromptLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。",

View file

@ -3720,10 +3720,10 @@
"indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "您的索引模式不匹配任何数据流、索引或索引别名,但{strongIndices}{matchedIndicesLength, plural, other {} }类似。",
"indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, one {个源} other {# 个源} }",
"indexPatternEditor.status.successLabel.successDetail": "您的索引模式匹配 {sourceCount} 个{sourceCount, plural, other {源} }。",
"indexPatternEditor.aliasLabel": "别名",
"dataViews.aliasLabel": "别名",
"indexPatternEditor.createIndex.noMatch": "名称必须匹配一个或多个数据流、索引或索引别名。",
"indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel": "--- 我不想使用时间筛选 ---",
"indexPatternEditor.dataStreamLabel": "数据流",
"dataViews.dataStreamLabel": "数据流",
"indexPatternEditor.dataView.unableSaveLabel": "无法保存数据视图。",
"indexPatternEditor.dataViewExists.ValidationErrorMessage": "具有此名称的数据视图已存在。",
"indexPatternEditor.editDataView.editConfirmationModal.confirmButton": "确认",
@ -3754,8 +3754,8 @@
"indexPatternEditor.form.customIndexPatternIdLabel": "定制数据视图 ID",
"indexPatternEditor.form.nameAriaLabel": "名称字段(可选)",
"indexPatternEditor.form.titleAriaLabel": "索引模式字段",
"indexPatternEditor.frozenLabel": "已冻结",
"indexPatternEditor.indexLabel": "索引",
"dataViews.frozenLabel": "已冻结",
"dataViews.indexLabel": "索引",
"indexPatternEditor.loadingHeader": "正在寻找匹配的索引......",
"indexPatternEditor.requireTimestampOption.ValidationErrorMessage": "选择时间戳字段。",
"indexPatternEditor.rollupDataView.createIndex.noMatchError": "汇总/打包数据视图错误:必须匹配一个汇总/打包索引",
@ -3763,7 +3763,7 @@
"indexPatternEditor.rollupDataView.warning.textParagraphOne": "Kibana 基于汇总/打包为数据视图提供公测版支持。将这些视图用于已保存搜索、可视化以及仪表板可能会遇到问题。某些高级功能,如 Timelion 和 Machine Learning不支持这些模式。",
"indexPatternEditor.rollupDataView.warning.textParagraphTwo": "可以根据一个汇总/打包索引和零个或更多常规索引匹配汇总/打包数据视图。汇总/打包数据视图的指标、字段、时间间隔和聚合有限。汇总/打包索引仅限于具有一个作业配置或多个作业配置兼容的索引。",
"indexPatternEditor.rollupIndexPattern.warning.title": "公测版功能",
"indexPatternEditor.rollupLabel": "汇总/打包",
"dataViews.rollupLabel": "汇总/打包",
"indexPatternEditor.saved": "已保存“{indexPatternName}”",
"indexPatternEditor.status.noSystemIndicesLabel": "没有数据流、索引或索引别名匹配您的索引模式。",
"indexPatternEditor.status.noSystemIndicesWithPromptLabel": "没有数据流、索引或索引别名匹配您的索引模式。",