[ML] Enhance support for counter fields in data visualizer / field statistics (#153893)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Quynh Nguyen (Quinn) 2023-04-04 17:14:25 -05:00 committed by GitHub
parent 8724518804
commit ab1ac1b25d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 318 additions and 528 deletions

View file

@ -35,4 +35,5 @@ export type { NumberValidationResult } from './src/validate_number';
export {
TIME_SERIES_METRIC_TYPES,
isCounterTimeSeriesMetric,
isGaugeTimeSeriesMetric,
} from './src/time_series_metric_fields';

View file

@ -24,3 +24,11 @@ export enum TIME_SERIES_METRIC_TYPES {
*/
export const isCounterTimeSeriesMetric = (field?: DataViewField) =>
field?.timeSeriesMetric === TIME_SERIES_METRIC_TYPES.COUNTER;
/**
* Check if DataViewField is a 'gauge' time series metric field
* @param field optional DataViewField
* @returns a boolean
*/
export const isGaugeTimeSeriesMetric = (field?: DataViewField) =>
field?.timeSeriesMetric === TIME_SERIES_METRIC_TYPES.GAUGE;

View file

@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import type { DocLinksStart } from '@kbn/core/public';
export const APP_ID = 'data_visualizer';
export const UI_SETTING_MAX_FILE_SIZE = 'fileUpload:maxFileSize';
@ -65,116 +64,3 @@ export const featureTitle = i18n.translate('xpack.dataVisualizer.title', {
defaultMessage: 'Upload a file',
});
export const featureId = `file_data_visualizer`;
const UNKNOWN_FIELD_TYPE_DESC = i18n.translate(
'xpack.dataVisualizer.index.fieldNameDescription.unknownField',
{
defaultMessage: 'Unknown field',
}
);
export function getFieldTypeDescription(type: string, docLinks: DocLinksStart) {
switch (type) {
case SUPPORTED_FIELD_TYPES.BOOLEAN:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.booleanField', {
defaultMessage: 'True and false values',
});
case SUPPORTED_FIELD_TYPES.CONFLICT:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.conflictField', {
defaultMessage: 'Field has values of different types. Resolve in Management > Data Views.',
});
case SUPPORTED_FIELD_TYPES.DATE:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.dateField', {
defaultMessage: 'A date string or the number of seconds or milliseconds since 1/1/1970',
});
case SUPPORTED_FIELD_TYPES.DATE_RANGE:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.dateRangeField', {
defaultMessage: 'Range of {dateFieldTypeLink} values. {viewSupportedDateFormatsLink}',
values: {
dateFieldTypeLink:
`<a href=${docLinks.links.discover.dateFieldTypeDocs}
target="_blank" rel="noopener">` +
i18n.translate(
'xpack.dataVisualizer.index.fieldNameDescription.dateRangeFieldLinkText',
{
defaultMessage: 'date',
}
) +
'</a>',
viewSupportedDateFormatsLink:
`<a href=${docLinks.links.discover.dateFormatsDocs}
target="_blank" rel="noopener">` +
i18n.translate(
'xpack.dataVisualizer.index.fieldNameDescription.viewSupportedDateFormatsLinkText',
{
defaultMessage: 'View supported date formats.',
}
) +
'</a>',
},
});
case SUPPORTED_FIELD_TYPES.GEO_POINT:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.geoPointField', {
defaultMessage: 'Latitude and longitude points',
});
case SUPPORTED_FIELD_TYPES.GEO_SHAPE:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.geoShapeField', {
defaultMessage: 'Complex shapes such as polygons',
});
case SUPPORTED_FIELD_TYPES.HISTOGRAM:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.histogramField', {
defaultMessage: 'Pre-aggregated numerical values in the form of a histogram',
});
case SUPPORTED_FIELD_TYPES.IP:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.ipAddressField', {
defaultMessage: 'IPv4 and IPv6 addresses',
});
case SUPPORTED_FIELD_TYPES.IP_RANGE:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.ipAddressRangeField', {
defaultMessage: 'Range of IP values supporting either IPv4 or IPv6 (or mixed) addresses',
});
case SUPPORTED_FIELD_TYPES.MURMUR3:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.murmur3Field', {
defaultMessage: 'Field that computes and stores hashes of values',
});
case SUPPORTED_FIELD_TYPES.NESTED:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.nestedField', {
defaultMessage: 'JSON object that preserves the relationship between its subfields',
});
case SUPPORTED_FIELD_TYPES.NUMBER:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.numberField', {
defaultMessage: 'Long, integer, short, byte, double, and float values',
});
case SUPPORTED_FIELD_TYPES.STRING:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.stringField', {
defaultMessage: 'Full text such as the body of an email or a product description',
});
case SUPPORTED_FIELD_TYPES.TEXT:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.textField', {
defaultMessage: 'Full text such as the body of an email or a product description',
});
case SUPPORTED_FIELD_TYPES.KEYWORD:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.keywordField', {
defaultMessage:
'Structured content such as an ID, email address, hostname, status code, or tag',
});
case SUPPORTED_FIELD_TYPES.VERSION:
return i18n.translate('xpack.dataVisualizer.index.fieldNameDescription.versionField', {
defaultMessage: 'Software versions. Supports {SemanticVersioningLink} precedence rules',
values: {
SemanticVersioningLink:
`<a href="https://semver.org/"
target="_blank" rel="noopener">` +
i18n.translate(
'xpack.dataVisualizer.index.advancedSettings.discover.fieldNameDescription.versionFieldLinkText',
{
defaultMessage: 'Semantic Versioning',
}
) +
'</a>',
},
});
default:
return UNKNOWN_FIELD_TYPE_DESC;
}
}

View file

@ -18,6 +18,7 @@ export interface FieldRequestConfig {
type: SupportedFieldType;
cardinality: number;
existsInDocs: boolean;
supportedAggs?: Set<string>;
}
export interface DocumentCountBuckets {

View file

@ -74,9 +74,9 @@ export const isIKibanaSearchResponse = (arg: unknown): arg is IKibanaSearchRespo
export interface NumericFieldStats {
fieldName: string;
count?: number;
min: number;
max: number;
avg: number;
min?: number;
max?: number;
avg?: number;
isTopValuesSampled: boolean;
topValues: Bucket[];
topValuesSampleSize: number;
@ -211,6 +211,8 @@ export interface FieldStatsCommonRequestParams {
samplingOption: SamplingOption;
}
export type SupportedAggs = Set<string>;
export interface OverallStatsSearchStrategyParams {
sessionId?: string;
earliest?: number;
@ -222,7 +224,10 @@ export interface OverallStatsSearchStrategyParams {
index: string;
timeFieldName?: string;
runtimeFieldMap?: estypes.MappingRuntimeFields;
aggregatableFields: string[];
aggregatableFields: Array<{
name: string;
supportedAggs: SupportedAggs;
}>;
nonAggregatableFields: string[];
fieldsToFetch?: string[];
browserSessionSeed: number;
@ -258,6 +263,7 @@ export interface Field {
type: string;
cardinality: number;
safeFieldName: string;
supportedAggs?: Set<string>;
}
export interface Aggs {

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import type { SupportedAggs } from './field_stats';
import type { Percentile, SupportedFieldType, FieldVisStats } from '.';
export interface MetricFieldVisStats {
avg?: number;
@ -27,16 +28,19 @@ export interface FieldVisConfig {
existsInDocs: boolean;
aggregatable: boolean;
loading: boolean;
secondaryType: string;
stats?: FieldVisStats;
fieldFormat?: any;
isUnsupportedType?: boolean;
deletable?: boolean;
supportedAggs?: SupportedAggs;
}
export interface FileBasedFieldVisConfig {
type: SupportedFieldType;
fieldName?: string;
displayName?: string;
secondaryType?: string;
stats?: FieldVisStats;
format?: string;
}

View file

@ -34,6 +34,7 @@
"esUiShared",
"fieldFormats",
"uiActions",
"unifiedFieldList",
"lens",
"cloudChat",
"savedSearch"

View file

@ -68,15 +68,24 @@ export function getNumberSettings(item: FieldVisConfig, defaultDataView: DataVie
return { columns, layer };
}
const operationType = item.supportedAggs?.has('avg') ? 'average' : 'max';
const operationLabel =
operationType === 'average'
? i18n.translate('xpack.dataVisualizer.index.lensChart.averageOfLabel', {
defaultMessage: 'Average of {fieldName}',
values: { fieldName: item.fieldName },
})
: i18n.translate('xpack.dataVisualizer.index.lensChart.maximumOfLabel', {
defaultMessage: 'Maximum of {fieldName}',
values: { fieldName: item.fieldName },
});
const columns: Record<string, GenericIndexPatternColumn> = {
col2: {
dataType: 'number',
isBucketed: false,
label: i18n.translate('xpack.dataVisualizer.index.lensChart.averageOfLabel', {
defaultMessage: 'Average of {fieldName}',
values: { fieldName: item.fieldName },
}),
operationType: 'average',
label: operationLabel,
operationType,
sourceField: item.fieldName!,
},
col1: {

View file

@ -9,18 +9,17 @@ import React, { FC } from 'react';
import { EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
import { getJobTypeLabel } from '../../util/field_types_utils';
import type { SupportedFieldType } from '../../../../../common/types';
import { getFieldTypeName } from '@kbn/unified-field-list-plugin/public';
import './_index.scss';
interface FieldTypeIconProps {
tooltipEnabled: boolean;
type: SupportedFieldType;
type: string;
}
export const FieldTypeIcon: FC<FieldTypeIconProps> = ({ tooltipEnabled = false, type }) => {
const label =
getJobTypeLabel(type) ??
getFieldTypeName(type) ??
i18n.translate('xpack.dataVisualizer.fieldTypeIcon.fieldTypeTooltip', {
defaultMessage: '{type} type',
values: { type },

View file

@ -8,6 +8,7 @@
import React, { FC, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { getFieldTypeName } from '@kbn/unified-field-list-plugin/public';
import { FieldTypesHelpPopover } from './field_types_help_popover';
import { MultiSelectPicker, Option } from '../multi_select_picker';
import type {
@ -15,7 +16,6 @@ import type {
FileBasedUnknownFieldVisConfig,
} from '../../../../../common/types/field_vis_config';
import { FieldTypeIcon } from '../field_type_icon';
import { jobTypeLabels } from '../../util/field_types_utils';
interface Props {
fields: Array<FileBasedFieldVisConfig | FileBasedUnknownFieldVisConfig>;
@ -40,9 +40,8 @@ export const DataVisualizerFieldTypesFilter: FC<Props> = ({
const fieldTypesTracker = new Set();
const fieldTypes: Option[] = [];
fields.forEach(({ type }) => {
if (type !== undefined && !fieldTypesTracker.has(type) && jobTypeLabels[type] !== undefined) {
const label = jobTypeLabels[type];
const label = getFieldTypeName(type);
if (type !== undefined && !fieldTypesTracker.has(type) && label !== undefined) {
fieldTypesTracker.add(type);
fieldTypes.push({
value: type,

View file

@ -22,7 +22,7 @@ import React, { FC, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
import { FormattedMessage } from '@kbn/i18n-react';
import { getFieldTypeDescription } from '../../../../../common/constants';
import { getFieldTypeDescription } from '@kbn/unified-field-list-plugin/public';
import { useDataVisualizerKibana } from '../../../kibana_context';
interface FieldTypeTableItem {
@ -49,9 +49,9 @@ export const FieldTypesHelpPopover: FC<{
fieldTypes.map((type, index) => ({
id: index,
dataType: type,
description: getFieldTypeDescription(type, docLinks),
description: getFieldTypeDescription(type),
})),
[fieldTypes, docLinks]
[fieldTypes]
);
const columnsSidebar = [

View file

@ -10,6 +10,11 @@ import { SUPPORTED_FIELD_TYPES } from '../../../../../common/constants';
interface CommonFieldConfig {
type: string;
fieldName?: string;
secondaryType?: string;
}
export function matchFieldType<T extends CommonFieldConfig>(fieldType: string, config: T) {
return fieldType === config.secondaryType || fieldType === config.type;
}
export function filterFields<T extends CommonFieldConfig>(
fields: T[],
@ -20,12 +25,12 @@ export function filterFields<T extends CommonFieldConfig>(
if (visibleFieldTypes && visibleFieldTypes.length > 0) {
items = items.filter(
(config) => visibleFieldTypes.findIndex((field) => field === config.type) > -1
(config) => visibleFieldTypes.findIndex((fieldType) => matchFieldType(fieldType, config)) > -1
);
}
if (visibleFieldNames && visibleFieldNames.length > 0) {
items = items.filter((config) => {
return visibleFieldNames.findIndex((field) => field === config.fieldName) > -1;
return visibleFieldNames.findIndex((fieldName) => fieldName === config.fieldName) > -1;
});
}

View file

@ -10,6 +10,7 @@ import { ES_FIELD_TYPES } from '@kbn/field-types';
import type { FindFileStructureResponse } from '@kbn/file-upload-plugin/common';
import type { SupportedFieldType } from '../../../../../common/types';
import { SUPPORTED_FIELD_TYPES } from '../../../../../common/constants';
export function getFieldNames(results: FindFileStructureResponse) {
const { mappings, field_stats: fieldStats, column_names: columnNames } = results;

View file

@ -17,6 +17,7 @@ import {
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { isDefined } from '@kbn/ml-is-defined';
import type { FieldDataRowProps } from '../../types/field_data_row';
import { kibanaFieldFormat, numberAsOrdinal } from '../../../utils';
import { MetricDistributionChart, buildChartDataFromStats } from '../metric_distribution_chart';
@ -58,16 +59,20 @@ export const NumberContent: FC<FieldDataRowProps> = ({ config, onAddFilter }) =>
),
value: kibanaFieldFormat(min, fieldFormat),
},
{
function: 'median',
display: (
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.fieldExpandedRow.numberContent.medianLabel"
defaultMessage="median"
/>
),
value: kibanaFieldFormat(median, fieldFormat),
},
...(isDefined(median)
? [
{
function: 'median',
display: (
<FormattedMessage
id="xpack.dataVisualizer.dataGrid.fieldExpandedRow.numberContent.medianLabel"
defaultMessage="median"
/>
),
value: kibanaFieldFormat(median, fieldFormat),
},
]
: []),
{
function: 'max',
display: (

View file

@ -193,8 +193,8 @@ export const DataVisualizerTable = <T extends DataVisualizerTableItem>({
name: i18n.translate('xpack.dataVisualizer.dataGrid.typeColumnName', {
defaultMessage: 'Type',
}),
render: (fieldType: SupportedFieldType) => {
return <FieldTypeIcon type={fieldType} tooltipEnabled={true} />;
render: (fieldType: SupportedFieldType, item: DataVisualizerTableItem) => {
return <FieldTypeIcon type={item.secondaryType ?? fieldType} tooltipEnabled={true} />;
},
width: dimensions.type,
sortable: true,
@ -396,7 +396,7 @@ export const DataVisualizerTable = <T extends DataVisualizerTableItem>({
boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}`,
},
'.euiTableRow > .euiTableRowCel': {
'border-top': 0,
borderTop: 0,
},
[useEuiMinBreakpoint('s')]: {
'& .columnHeader__title': {

View file

@ -54,6 +54,7 @@ export const TopValues: FC<Props> = ({ stats, fieldFormat, barColor, compressed,
if (stats === undefined || !stats.topValues) return null;
const { topValues, fieldName, sampleCount } = stats;
if (topValues?.length === 0) return null;
const totalDocuments = stats.totalDocuments ?? sampleCount ?? 0;
const topValuesOtherCountPercent =
1 - (topValues ? topValues.reduce((acc, bucket) => acc + bucket.percent, 0) : 0);

View file

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SUPPORTED_FIELD_TYPES } from '../../../../common/constants';
import { getJobTypeLabel, jobTypeLabels } from './field_types_utils';
describe('field type utils', () => {
describe('getJobTypeLabel: Getting a field type aria label by passing what it is stored in constants', () => {
test('should returns all SUPPORTED_FIELD_TYPES labels exactly as it is for each correct value', () => {
const keys = Object.keys(SUPPORTED_FIELD_TYPES);
const receivedLabels: Record<string, string | null> = {};
const testStorage = jobTypeLabels;
keys.forEach((key) => {
const constant = key as keyof typeof SUPPORTED_FIELD_TYPES;
receivedLabels[SUPPORTED_FIELD_TYPES[constant]] = getJobTypeLabel(
SUPPORTED_FIELD_TYPES[constant]
);
});
expect(receivedLabels).toEqual(testStorage);
});
test('should returns NULL as SUPPORTED_FIELD_TYPES does not contain such a keyword', () => {
expect(getJobTypeLabel('SUPPORTED_FIELD_TYPES')).toBe(null);
});
});
});

View file

@ -5,116 +5,15 @@
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { DataViewField } from '@kbn/data-views-plugin/public';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types';
import type { SupportedFieldType } from '../../../../common/types';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import { getFieldType } from '@kbn/unified-field-list-plugin/public';
import { SUPPORTED_FIELD_TYPES } from '../../../../common/constants';
export const getJobTypeLabel = (type: string) => {
return type in jobTypeLabels ? jobTypeLabels[type as keyof typeof jobTypeLabels] : null;
};
export const jobTypeLabels: Record<SupportedFieldType, string> = {
[SUPPORTED_FIELD_TYPES.BOOLEAN]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.booleanTypeLabel',
{
defaultMessage: 'Boolean',
}
),
[SUPPORTED_FIELD_TYPES.CONFLICT]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.conflictTypeLabel',
{
defaultMessage: 'Conflict',
}
),
[SUPPORTED_FIELD_TYPES.DATE]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.dateTypeLabel', {
defaultMessage: 'Date',
}),
[SUPPORTED_FIELD_TYPES.DATE_RANGE]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.dateRangeTypeLabel',
{
defaultMessage: 'Date range',
}
),
[SUPPORTED_FIELD_TYPES.GEO_POINT]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.geoPointTypeLabel',
{
defaultMessage: 'Geo point',
}
),
[SUPPORTED_FIELD_TYPES.GEO_SHAPE]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.geoShapeTypeLabel',
{
defaultMessage: 'Geo shape',
}
),
[SUPPORTED_FIELD_TYPES.HISTOGRAM]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.histogramTypeLabel',
{
defaultMessage: 'Histogram',
}
),
[SUPPORTED_FIELD_TYPES.IP]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.ipTypeLabel', {
defaultMessage: 'IP',
}),
[SUPPORTED_FIELD_TYPES.IP_RANGE]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.ipRangeTypeLabel',
{
defaultMessage: 'IP range',
}
),
[SUPPORTED_FIELD_TYPES.MURMUR3]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.murmur3TypeLabel',
{
defaultMessage: 'Murmur3',
}
),
[SUPPORTED_FIELD_TYPES.NESTED]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.numberTypeLabel',
{
defaultMessage: 'Number',
}
),
[SUPPORTED_FIELD_TYPES.NUMBER]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.numberTypeLabel',
{
defaultMessage: 'Number',
}
),
[SUPPORTED_FIELD_TYPES.STRING]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.stringTypeLabel',
{
defaultMessage: 'String',
}
),
[SUPPORTED_FIELD_TYPES.TEXT]: i18n.translate('xpack.dataVisualizer.fieldTypeIcon.textTypeLabel', {
defaultMessage: 'Text',
}),
[SUPPORTED_FIELD_TYPES.KEYWORD]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.keywordTypeLabel',
{
defaultMessage: 'Keyword',
}
),
[SUPPORTED_FIELD_TYPES.VERSION]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.versionTypeLabel',
{
defaultMessage: 'Version',
}
),
[SUPPORTED_FIELD_TYPES.UNKNOWN]: i18n.translate(
'xpack.dataVisualizer.fieldTypeIcon.unknownTypeLabel',
{
defaultMessage: 'Unknown',
}
),
};
// convert kibana types to ML Job types
// this is needed because kibana types only have string and not text and keyword.
// and we can't use ES_FIELD_TYPES because it has no NUMBER type
export function kbnTypeToJobType(field: DataViewField) {
export function kbnTypeToSupportedType(field: DataViewField) {
// Return undefined if not one of the supported data visualizer field types.
let type;
@ -126,32 +25,9 @@ export function kbnTypeToJobType(field: DataViewField) {
type = SUPPORTED_FIELD_TYPES.VERSION;
}
break;
case KBN_FIELD_TYPES.NUMBER:
if (field.esTypes?.some((d) => d === ES_FIELD_TYPES.AGGREGATE_METRIC_DOUBLE)) {
break;
}
type = SUPPORTED_FIELD_TYPES.NUMBER;
break;
case KBN_FIELD_TYPES.DATE:
type = SUPPORTED_FIELD_TYPES.DATE;
break;
case KBN_FIELD_TYPES.IP:
type = SUPPORTED_FIELD_TYPES.IP;
break;
case KBN_FIELD_TYPES.BOOLEAN:
type = SUPPORTED_FIELD_TYPES.BOOLEAN;
break;
case KBN_FIELD_TYPES.GEO_POINT:
type = SUPPORTED_FIELD_TYPES.GEO_POINT;
break;
case KBN_FIELD_TYPES.GEO_SHAPE:
type = SUPPORTED_FIELD_TYPES.GEO_SHAPE;
break;
case KBN_FIELD_TYPES.HISTOGRAM:
type = SUPPORTED_FIELD_TYPES.HISTOGRAM;
break;
default:
type = getFieldType(field);
break;
}

View file

@ -37,6 +37,7 @@ import {
import { useStorage } from '@kbn/ml-local-storage';
import type { SavedSearch } from '@kbn/saved-search-plugin/public';
import { kbnTypeToSupportedType } from '../../../common/util/field_types_utils';
import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme';
import {
DV_FROZEN_TIER_PREFERENCE,
@ -59,12 +60,10 @@ import {
DataVisualizerIndexBasedPageUrlState,
} from '../../types/index_data_visualizer_state';
import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage } from '../../types/combined_query';
import type { SupportedFieldType } from '../../../../../common/types';
import { useDataVisualizerKibana } from '../../../kibana_context';
import { FieldCountPanel } from '../../../common/components/field_count_panel';
import { DocumentCountContent } from '../../../common/components/document_count_content';
import { OMIT_FIELDS } from '../../../../../common/constants';
import { kbnTypeToJobType } from '../../../common/util/field_types_utils';
import { SearchPanel } from '../search_panel';
import { ActionsPanel } from '../actions_panel';
import { createMergedEsQuery } from '../../utils/saved_search_utils';
@ -214,10 +213,10 @@ export const IndexDataVisualizerView: FC<IndexDataVisualizerViewProps> = (dataVi
const fieldTypes = useMemo(() => {
// Obtain the list of non metric field types which appear in the index pattern.
const indexedFieldTypes: SupportedFieldType[] = [];
const indexedFieldTypes: string[] = [];
dataViewFields.forEach((field) => {
if (!OMIT_FIELDS.includes(field.name) && field.scripted !== true) {
const dataVisualizerType: SupportedFieldType | undefined = kbnTypeToJobType(field);
const dataVisualizerType = kbnTypeToSupportedType(field);
if (dataVisualizerType !== undefined && !indexedFieldTypes.includes(dataVisualizerType)) {
indexedFieldTypes.push(dataVisualizerType);
}

View file

@ -9,22 +9,21 @@ import React, { FC, useMemo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { getFieldTypeName } from '@kbn/unified-field-list-plugin/public';
import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme';
import { FieldTypesHelpPopover } from '../../../common/components/field_types_filter/field_types_help_popover';
import type { SupportedFieldType } from '../../../../../common/types';
import { FieldTypeIcon } from '../../../common/components/field_type_icon';
import { MultiSelectPicker, Option } from '../../../common/components/multi_select_picker';
import { jobTypeLabels } from '../../../common/util/field_types_utils';
export const DataVisualizerFieldTypeFilter: FC<{
indexedFieldTypes: SupportedFieldType[];
indexedFieldTypes: string[];
setVisibleFieldTypes(q: string[]): void;
visibleFieldTypes: string[];
}> = ({ indexedFieldTypes, setVisibleFieldTypes, visibleFieldTypes }) => {
const euiTheme = useCurrentEuiTheme();
const options: Option[] = useMemo(() => {
return indexedFieldTypes.map((indexedFieldName) => {
const label = jobTypeLabels[indexedFieldName] ?? '';
const label = getFieldTypeName(indexedFieldName) ?? indexedFieldName;
return {
value: indexedFieldName,

View file

@ -21,7 +21,6 @@ import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import { isDefined } from '@kbn/ml-is-defined';
import { DataVisualizerFieldNamesFilter } from './field_name_filter';
import { DataVisualizerFieldTypeFilter } from './field_type_filter';
import type { SupportedFieldType } from '../../../../../common/types';
import { SearchQueryLanguage } from '../../types/combined_query';
import { useDataVisualizerKibana } from '../../../kibana_context';
import { createMergedEsQuery } from '../../utils/saved_search_utils';
@ -33,7 +32,7 @@ interface Props {
searchQuery: Query['query'];
searchQueryLanguage: SearchQueryLanguage;
overallStats: OverallStats;
indexedFieldTypes: SupportedFieldType[];
indexedFieldTypes: string[];
setVisibleFieldTypes(q: string[]): void;
visibleFieldTypes: string[];
setVisibleFieldNames(q: string[]): void;

View file

@ -16,7 +16,7 @@ import seedrandom from 'seedrandom';
import type { SamplingOption } from '@kbn/discover-plugin/public/application/main/components/field_stats_table/field_stats_table';
import type { Dictionary } from '@kbn/ml-url-state';
import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker';
import { isCounterTimeSeriesMetric } from '@kbn/ml-agg-utils';
import { filterFields } from '../../common/components/fields_stats_grid/filter_fields';
import type { RandomSamplerOption } from '../constants/random_sampler';
import type { DataVisualizerIndexBasedAppState } from '../types/index_data_visualizer_state';
import { useDataVisualizerKibana } from '../../kibana_context';
@ -25,12 +25,12 @@ import type { MetricFieldsStats } from '../../common/components/stats_table/comp
import { TimeBuckets } from '../../../../common/services/time_buckets';
import type { FieldVisConfig } from '../../common/components/stats_table/types';
import {
SUPPORTED_FIELD_TYPES,
NON_AGGREGATABLE_FIELD_TYPES,
OMIT_FIELDS,
SUPPORTED_FIELD_TYPES,
} from '../../../../common/constants';
import type { FieldRequestConfig, SupportedFieldType } from '../../../../common/types';
import { kbnTypeToJobType } from '../../common/util/field_types_utils';
import { kbnTypeToSupportedType } from '../../common/util/field_types_utils';
import { getActions } from '../../common/components/field_data_row/action_menu';
import type { DataVisualizerGridInput } from '../embeddables/grid_embeddable/grid_embeddable';
import { getDefaultPageState } from '../components/index_data_visualizer_view/index_data_visualizer_view';
@ -38,6 +38,7 @@ import { useFieldStatsSearchStrategy } from './use_field_stats';
import { useOverallStats } from './use_overall_stats';
import type { OverallStatsSearchStrategyParams } from '../../../../common/types/field_stats';
import type { AggregatableField, NonAggregatableField } from '../types/overall_stats';
import { getSupportedAggs } from '../utils/get_supported_aggs';
const defaults = getDefaultPageState();
@ -197,7 +198,7 @@ export const useDataVisualizerGridData = (
const aggInterval = buckets.getInterval();
const aggregatableFields: string[] = [];
const aggregatableFields: OverallStatsSearchStrategyParams['aggregatableFields'] = [];
const nonAggregatableFields: string[] = [];
const fields = currentDataView.fields;
@ -212,7 +213,7 @@ export const useDataVisualizerGridData = (
!NON_AGGREGATABLE_FIELD_TYPES.has(field.type) &&
!field.esTypes?.some((d) => d === ES_FIELD_TYPES.AGGREGATE_METRIC_DOUBLE)
) {
aggregatableFields.push(field.name);
aggregatableFields.push({ name: field.name, supportedAggs: getSupportedAggs(field) });
} else {
nonAggregatableFields.push(field.name);
}
@ -265,10 +266,8 @@ export const useDataVisualizerGridData = (
const existMetricFields = metricConfigs
.map((config) => {
return {
fieldName: config.fieldName,
type: config.type,
cardinality: config.stats?.cardinality ?? 0,
existsInDocs: config.existsInDocs,
...config,
cardinality: config.stats?.cardinality,
};
})
.filter((c) => c !== undefined) as FieldRequestConfig[];
@ -278,10 +277,8 @@ export const useDataVisualizerGridData = (
const existNonMetricFields: FieldRequestConfig[] = nonMetricConfigs
.map((config) => {
return {
fieldName: config.fieldName,
type: config.type,
cardinality: config.stats?.cardinality ?? 0,
existsInDocs: config.existsInDocs,
...config,
cardinality: config.stats?.cardinality,
};
})
.filter((c) => c !== undefined) as FieldRequestConfig[];
@ -372,9 +369,11 @@ export const useDataVisualizerGridData = (
...fieldData,
fieldFormat: currentDataView.getFormatterForField(field),
type: SUPPORTED_FIELD_TYPES.NUMBER,
secondaryType: kbnTypeToSupportedType(field),
loading: fieldData?.existsInDocs ?? true,
aggregatable: true,
deletable: field.runtimeField !== undefined,
supportedAggs: getSupportedAggs(field),
};
if (field.displayName !== metricConfig.fieldName) {
metricConfig.displayName = field.displayName;
@ -393,7 +392,7 @@ export const useDataVisualizerGridData = (
const createNonMetricCards = useCallback(() => {
const allNonMetricFields = dataViewFields.filter((f) => {
return (
(f.type !== KBN_FIELD_TYPES.NUMBER || isCounterTimeSeriesMetric(f)) &&
f.type !== KBN_FIELD_TYPES.NUMBER &&
f.displayName !== undefined &&
isDisplayField(f.displayName) === true
);
@ -448,6 +447,7 @@ export const useDataVisualizerGridData = (
const fieldData = nonMetricFieldData.find((f) => f.fieldName === field.spec.name);
const nonMetricConfig: Partial<FieldVisConfig> = {
...(fieldData ? fieldData : {}),
secondaryType: kbnTypeToSupportedType(field),
fieldFormat: currentDataView.getFormatterForField(field),
aggregatable: field.aggregatable,
loading: fieldData?.existsInDocs ?? true,
@ -456,7 +456,7 @@ export const useDataVisualizerGridData = (
// Map the field type from the Kibana index pattern to the field type
// used in the data visualizer.
const dataVisualizerType = kbnTypeToJobType(field);
const dataVisualizerType = kbnTypeToSupportedType(field) as SupportedFieldType;
if (dataVisualizerType !== undefined) {
nonMetricConfig.type = dataVisualizerType;
} else {
@ -486,16 +486,12 @@ export const useDataVisualizerGridData = (
() => {
const fieldStats = strategyResponse.fieldStats;
let combinedConfigs = [...nonMetricConfigs, ...metricConfigs];
if (visibleFieldTypes && visibleFieldTypes.length > 0) {
combinedConfigs = combinedConfigs.filter(
(config) => visibleFieldTypes.findIndex((field) => field === config.type) > -1
);
}
if (visibleFieldNames && visibleFieldNames.length > 0) {
combinedConfigs = combinedConfigs.filter(
(config) => visibleFieldNames.findIndex((field) => field === config.fieldName) > -1
);
}
combinedConfigs = filterFields(
combinedConfigs,
visibleFieldNames,
visibleFieldTypes
).filteredFields;
if (fieldStats) {
combinedConfigs = combinedConfigs.map((c) => {

View file

@ -177,9 +177,7 @@ export function useFieldStatsSearchStrategy(
const batches = createBatchedRequests(
pageOfConfigs.map((config, idx) => ({
fieldName: config.fieldName,
type: config.type,
cardinality: config.cardinality,
...config,
safeFieldName: getSafeAggregationName(config.fieldName, idx),
})),
10

View file

@ -7,8 +7,11 @@
import type { Observable } from 'rxjs';
import type { ISearchOptions } from '@kbn/data-plugin/common';
import { ISearchStart } from '@kbn/data-plugin/public';
import type { FieldStatsCommonRequestParams } from '../../../../../common/types/field_stats';
import type { ISearchStart } from '@kbn/data-plugin/public';
import type {
FieldStatsCommonRequestParams,
SupportedAggs,
} from '../../../../../common/types/field_stats';
import type { FieldStatsError } from '../../../../../common/types/field_stats';
import type { FieldStats } from '../../../../../common/types/field_stats';
import { SUPPORTED_FIELD_TYPES } from '../../../../../common/constants';
@ -26,6 +29,7 @@ export const getFieldsStats = (
type: string;
cardinality: number;
safeFieldName: string;
supportedAggs?: SupportedAggs;
}>,
options: ISearchOptions
): Observable<FieldStats[] | FieldStatsError> | undefined => {

View file

@ -58,35 +58,58 @@ export const getNumericFieldsStatsRequest = (
const aggs: Aggs = {};
fields.forEach((field, i) => {
const { safeFieldName } = field;
const { safeFieldName, supportedAggs } = field;
aggs[`${safeFieldName}_field_stats`] = {
filter: { exists: { field: field.fieldName } },
aggs: {
actual_stats: {
stats: { field: field.fieldName },
const include = !isDefined(supportedAggs);
if (include || supportedAggs.has('stats')) {
aggs[`${safeFieldName}_field_stats`] = {
filter: { exists: { field: field.fieldName } },
aggs: {
actual_stats: {
stats: { field: field.fieldName },
},
},
},
};
aggs[`${safeFieldName}_percentiles`] = {
percentiles: {
field: field.fieldName,
percents,
keyed: false,
},
};
};
}
const top = {
terms: {
field: field.fieldName,
size: 10,
order: {
_count: 'desc',
if (include || (supportedAggs.has('min') && supportedAggs.has('max'))) {
aggs[`${safeFieldName}_min_max`] = {
filter: { exists: { field: field.fieldName } },
aggs: {
min: {
min: { field: field.fieldName },
},
max: {
max: { field: field.fieldName },
},
},
} as AggregationsTermsAggregation,
};
};
}
aggs[`${safeFieldName}_top`] = top;
if (include || supportedAggs.has('percentiles')) {
aggs[`${safeFieldName}_percentiles`] = {
percentiles: {
field: field.fieldName,
percents,
keyed: false,
},
};
}
if (include || supportedAggs.has('terms')) {
const top = {
terms: {
field: field.fieldName,
size: 10,
order: {
_count: 'desc',
},
} as AggregationsTermsAggregation,
};
aggs[`${safeFieldName}_top`] = top;
}
});
const searchBody = {
@ -102,6 +125,29 @@ export const getNumericFieldsStatsRequest = (
};
};
const processStats = (safeFieldName: string, aggregations: object, aggsPath: string[]) => {
const fieldStatsResp = get(
aggregations,
[...aggsPath, `${safeFieldName}_field_stats`, 'actual_stats'],
undefined
);
if (fieldStatsResp) {
return {
min: get(fieldStatsResp, 'min'),
max: get(fieldStatsResp, 'max'),
avg: get(fieldStatsResp, 'avg'),
};
}
const minMaxResp = get(aggregations, [...aggsPath, `${safeFieldName}_min_max`], {});
if (minMaxResp) {
return {
min: get(minMaxResp, ['min', 'value']),
max: get(minMaxResp, ['max', 'value']),
};
}
return {};
};
export const fetchNumericFieldsStats = (
dataSearch: ISearchStart,
params: FieldStatsCommonRequestParams,
@ -135,11 +181,6 @@ export const fetchNumericFieldsStats = (
[...aggsPath, `${safeFieldName}_field_stats`, 'doc_count'],
0
);
const fieldStatsResp = get(
aggregations,
[...aggsPath, `${safeFieldName}_field_stats`, 'actual_stats'],
{}
);
const topAggsPath = [...aggsPath, `${safeFieldName}_top`];
if (samplerShardSize < 1 && field.cardinality >= SAMPLER_TOP_TERMS_THRESHOLD) {
@ -151,9 +192,7 @@ export const fetchNumericFieldsStats = (
const stats: NumericFieldStats = {
fieldName: field.fieldName,
min: get(fieldStatsResp, 'min', 0),
max: get(fieldStatsResp, 'max', 0),
avg: get(fieldStatsResp, 'avg', 0),
...processStats(safeFieldName, aggregations, aggsPath),
isTopValuesSampled:
isNormalSamplingOption(params.samplingOption) ||
(isDefined(params.samplingProbability) && params.samplingProbability < 1),
@ -168,15 +207,21 @@ export const fetchNumericFieldsStats = (
[...aggsPath, `${safeFieldName}_percentiles`, 'values'],
[]
);
const medianPercentile: { value: number; key: number } | undefined = find(percentiles, {
key: 50,
});
stats.median = medianPercentile !== undefined ? medianPercentile!.value : 0;
stats.distribution = processDistributionData(
percentiles,
PERCENTILE_SPACING,
stats.min
);
if (percentiles && isDefined(stats.min)) {
const medianPercentile: { value: number; key: number } | undefined = find(
percentiles,
{
key: 50,
}
);
stats.median = medianPercentile !== undefined ? medianPercentile!.value : 0;
stats.distribution = processDistributionData(
percentiles,
PERCENTILE_SPACING,
stats.min
);
}
}
batchStats.push(stats);

View file

@ -7,20 +7,24 @@
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { get } from 'lodash';
import { Query } from '@kbn/es-query';
import type { Query } from '@kbn/es-query';
import type { IKibanaSearchResponse } from '@kbn/data-plugin/common';
import type { AggCardinality } from '@kbn/ml-agg-utils';
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
import { buildBaseFilterCriteria, getSafeAggregationName } from '@kbn/ml-query-utils';
import { buildAggregationWithSamplingOption } from './build_random_sampler_agg';
import { getDatafeedAggregations } from '../../../../../common/utils/datafeed_utils';
import { AggregatableField, NonAggregatableField } from '../../types/overall_stats';
import { Aggs, SamplingOption } from '../../../../../common/types/field_stats';
import type { AggregatableField, NonAggregatableField } from '../../types/overall_stats';
import type {
Aggs,
OverallStatsSearchStrategyParams,
SamplingOption,
} from '../../../../../common/types/field_stats';
export const checkAggregatableFieldsExistRequest = (
dataViewTitle: string,
query: Query['query'],
aggregatableFields: string[],
aggregatableFields: OverallStatsSearchStrategyParams['aggregatableFields'],
samplingOption: SamplingOption,
timeFieldName: string | undefined,
earliestMs?: number,
@ -45,23 +49,28 @@ export const checkAggregatableFieldsExistRequest = (
: {}),
};
aggregatableFields.forEach((field, i) => {
aggregatableFields.forEach(({ name: field, supportedAggs }, i) => {
const safeFieldName = getSafeAggregationName(field, i);
aggs[`${safeFieldName}_count`] = {
filter: { exists: { field } },
};
let cardinalityField: AggCardinality;
if (datafeedConfig?.script_fields?.hasOwnProperty(field)) {
cardinalityField = aggs[`${safeFieldName}_cardinality`] = {
cardinality: { script: datafeedConfig?.script_fields[field].script },
};
} else {
cardinalityField = {
cardinality: { field },
if (supportedAggs.has('count')) {
aggs[`${safeFieldName}_count`] = {
filter: { exists: { field } },
};
}
aggs[`${safeFieldName}_cardinality`] = cardinalityField;
if (supportedAggs.has('cardinality')) {
let cardinalityField: AggCardinality;
if (datafeedConfig?.script_fields?.hasOwnProperty(field)) {
cardinalityField = aggs[`${safeFieldName}_cardinality`] = {
cardinality: { script: datafeedConfig?.script_fields[field].script },
};
} else {
cardinalityField = {
cardinality: { field },
};
}
aggs[`${safeFieldName}_cardinality`] = cardinalityField;
}
});
const searchBody = {
@ -88,7 +97,7 @@ export const checkAggregatableFieldsExistRequest = (
};
export interface AggregatableFieldOverallStats extends IKibanaSearchResponse {
aggregatableFields: string[];
aggregatableFields: OverallStatsSearchStrategyParams['aggregatableFields'];
}
export type NonAggregatableFieldOverallStats = IKibanaSearchResponse;
@ -107,7 +116,7 @@ export function isNonAggregatableFieldOverallStats(
export const processAggregatableFieldsExistResponse = (
responses: AggregatableFieldOverallStats[] | undefined,
aggregatableFields: string[],
aggregatableFields: OverallStatsSearchStrategyParams['aggregatableFields'],
datafeedConfig?: estypes.MlDatafeed
) => {
const stats = {
@ -122,7 +131,7 @@ export const processAggregatableFieldsExistResponse = (
const aggsPath = ['sample'];
const sampleCount = aggregations.sample.doc_count;
aggregatableFieldsChunk.forEach((field, i) => {
aggregatableFieldsChunk.forEach(({ name: field, supportedAggs }, i) => {
const safeFieldName = getSafeAggregationName(field, i);
// Sampler agg will yield doc_count that's bigger than the actual # of sampled records
// because it uses the stored _doc_count if available
@ -132,11 +141,11 @@ export const processAggregatableFieldsExistResponse = (
const multiplier =
count > sampleCount ? get(aggregations, [...aggsPath, 'probability'], 1) : 1;
if (count > 0) {
const cardinality = get(
aggregations,
[...aggsPath, `${safeFieldName}_cardinality`, 'value'],
0
);
const cardinality = get(aggregations, [
...aggsPath,
`${safeFieldName}_cardinality`,
'value',
]);
stats.aggregatableExistsFields.push({
fieldName: field,
existsInDocs: true,
@ -151,11 +160,11 @@ export const processAggregatableFieldsExistResponse = (
datafeedConfig?.script_fields?.hasOwnProperty(field) ||
datafeedConfig?.runtime_mappings?.hasOwnProperty(field)
) {
const cardinality = get(
aggregations,
[...aggsPath, `${safeFieldName}_cardinality`, 'value'],
0
);
const cardinality = get(aggregations, [
...aggsPath,
`${safeFieldName}_cardinality`,
'value',
]);
stats.aggregatableExistsFields.push({
fieldName: field,
existsInDocs: true,

View file

@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { DataViewField } from '@kbn/data-views-plugin/common';
import { isCounterTimeSeriesMetric, isGaugeTimeSeriesMetric } from '@kbn/ml-agg-utils';
/**
* Partial list of supported ES aggs that are used by Index data visualizer/Field stats
*/
const SUPPORTED_AGGS = {
COUNTER: new Set([
'count',
'histogram',
'variable_width_histogram',
'rate',
'min',
'max',
'top_metrics',
'range',
]),
GAUGE: new Set([
'count',
'max',
'top_metrics',
'missing',
'date_histogram',
'sum',
'rate',
'boxplot',
'value_count',
'avg',
'percentiles',
'cardinality',
'histogram',
'variable_width_histogram',
'frequent_item_sets',
'min',
'stats',
'diversified_sampler',
'percentile_ranks',
'median_absolute_deviation',
'multi_terms',
'auto_date_histogram',
'rare_terms',
'range',
'extended_stats',
'date_range',
'terms',
'significant_terms',
]),
AGGREGATABLE: new Set([
'count',
'avg',
'cardinality',
'histogram',
'percentiles',
'stats',
'terms',
]),
DEFAULT: new Set<string>(),
};
/**
* Temporarily add list of supported ES aggs until the PR below is merged
* https://github.com/elastic/elasticsearch/pull/93884
*/
export const getSupportedAggs = (field: DataViewField) => {
if (isCounterTimeSeriesMetric(field)) return SUPPORTED_AGGS.COUNTER;
if (isGaugeTimeSeriesMetric(field)) return SUPPORTED_AGGS.GAUGE;
if (field.aggregatable) return SUPPORTED_AGGS.AGGREGATABLE;
return SUPPORTED_AGGS.DEFAULT;
};

View file

@ -57,6 +57,7 @@
"@kbn/ml-is-defined",
"@kbn/ml-query-utils",
"@kbn/saved-search-plugin",
"@kbn/unified-field-list-plugin",
],
"exclude": [
"target/**/*",

View file

@ -10816,8 +10816,6 @@
"xpack.dataVisualizer.index.dataLoader.internalServerErrorMessage": "Erreur lors du chargement des données dans l'index {index}. {message}. La requête a peut-être expiré. Essayez d'utiliser un échantillon d'une taille inférieure ou de réduire la plage temporelle.",
"xpack.dataVisualizer.index.dataViewNotBasedOnTimeSeriesNotificationTitle": "La vue de données {dataViewTitle} n'est pas basée sur une série temporelle",
"xpack.dataVisualizer.index.errorLoadingDataMessage": "Erreur lors du chargement des données dans l'index {index}. {message}.",
"xpack.dataVisualizer.index.fieldNameDescription.dateRangeField": "Plage de valeurs {dateFieldTypeLink}. {viewSupportedDateFormatsLink}",
"xpack.dataVisualizer.index.fieldNameDescription.versionField": "Versions des logiciels. Prend en charge les règles de priorité de {SemanticVersioningLink}",
"xpack.dataVisualizer.index.lensChart.averageOfLabel": "Moyenne de {fieldName}",
"xpack.dataVisualizer.index.lensChart.chartTitle": "Lens pour {fieldName}",
"xpack.dataVisualizer.index.savedSearchErrorMessage": "Erreur lors de la récupération de la recherche enregistrée {savedSearchId}",
@ -10882,22 +10880,6 @@
"xpack.dataVisualizer.fieldStats.maxTitle": "max",
"xpack.dataVisualizer.fieldStats.medianTitle": "médiane",
"xpack.dataVisualizer.fieldStats.minTitle": "min",
"xpack.dataVisualizer.fieldTypeIcon.booleanTypeLabel": "Booléen",
"xpack.dataVisualizer.fieldTypeIcon.conflictTypeLabel": "Conflit",
"xpack.dataVisualizer.fieldTypeIcon.dateRangeTypeLabel": "Plage de dates",
"xpack.dataVisualizer.fieldTypeIcon.dateTypeLabel": "Date",
"xpack.dataVisualizer.fieldTypeIcon.geoPointTypeLabel": "Point géographique",
"xpack.dataVisualizer.fieldTypeIcon.geoShapeTypeLabel": "Forme géométrique",
"xpack.dataVisualizer.fieldTypeIcon.histogramTypeLabel": "Histogramme",
"xpack.dataVisualizer.fieldTypeIcon.ipRangeTypeLabel": "Plage d'IP",
"xpack.dataVisualizer.fieldTypeIcon.ipTypeLabel": "IP",
"xpack.dataVisualizer.fieldTypeIcon.keywordTypeLabel": "Mot-clé",
"xpack.dataVisualizer.fieldTypeIcon.murmur3TypeLabel": "Murmur3",
"xpack.dataVisualizer.fieldTypeIcon.numberTypeLabel": "Nombre",
"xpack.dataVisualizer.fieldTypeIcon.stringTypeLabel": "Chaîne",
"xpack.dataVisualizer.fieldTypeIcon.textTypeLabel": "Texte",
"xpack.dataVisualizer.fieldTypeIcon.unknownTypeLabel": "Inconnu",
"xpack.dataVisualizer.fieldTypeIcon.versionTypeLabel": "Version",
"xpack.dataVisualizer.fieldTypeSelect": "Type du champ",
"xpack.dataVisualizer.fieldTypesPopover.buttonAriaLabel": "Aide sur le type de filtre",
"xpack.dataVisualizer.fieldTypesPopover.dataTypeColumnTitle": "Type de données",
@ -11041,7 +11023,6 @@
"xpack.dataVisualizer.index.actionsPanel.discoverAppTitle": "Découverte",
"xpack.dataVisualizer.index.actionsPanel.exploreTitle": "Explorer vos données",
"xpack.dataVisualizer.index.actionsPanel.viewIndexInDiscoverDescription": "Explorez les documents de votre index.",
"xpack.dataVisualizer.index.advancedSettings.discover.fieldNameDescription.versionFieldLinkText": "Gestion sémantique des versions",
"xpack.dataVisualizer.index.components.grid.description": "Visualiser les données",
"xpack.dataVisualizer.index.components.grid.displayName": "Grille du visualiseur de données",
"xpack.dataVisualizer.index.dataGrid.actionsColumnLabel": "Actions",
@ -11062,23 +11043,6 @@
"xpack.dataVisualizer.index.embeddableErrorTitle": "Erreur lors du chargement de l'incorporable",
"xpack.dataVisualizer.index.embeddableNoResultsMessage": "Résultat introuvable",
"xpack.dataVisualizer.index.errorFetchingFieldStatisticsMessage": "Erreur lors de la récupération des statistiques de champ",
"xpack.dataVisualizer.index.fieldNameDescription.booleanField": "Valeurs vraies ou fausses",
"xpack.dataVisualizer.index.fieldNameDescription.conflictField": "Le champ possède des valeurs de différents types. Corrigez le problème dans Gestion > Vues de données.",
"xpack.dataVisualizer.index.fieldNameDescription.dateField": "Chaîne de date ou nombre de secondes ou de millisecondes depuis 1/1/1970",
"xpack.dataVisualizer.index.fieldNameDescription.dateRangeFieldLinkText": "date",
"xpack.dataVisualizer.index.fieldNameDescription.geoPointField": "Points de latitude et de longitude",
"xpack.dataVisualizer.index.fieldNameDescription.geoShapeField": "Formes complexes, telles que des polygones",
"xpack.dataVisualizer.index.fieldNameDescription.histogramField": "Valeurs numériques pré-agrégées sous forme d'histogramme",
"xpack.dataVisualizer.index.fieldNameDescription.ipAddressField": "Adresses IPv4 et IPv6",
"xpack.dataVisualizer.index.fieldNameDescription.ipAddressRangeField": "Plage de valeurs IP prenant en charge les adresses IPv4 ou IPv6 (ou les 2)",
"xpack.dataVisualizer.index.fieldNameDescription.keywordField": "Contenu structuré tel qu'un ID, une adresse e-mail, un nom d'hôte, un code de statut, ou une balise",
"xpack.dataVisualizer.index.fieldNameDescription.murmur3Field": "Champ qui calcule et stocke les hachages de valeurs",
"xpack.dataVisualizer.index.fieldNameDescription.nestedField": "Objet JSON qui conserve la relation entre ses sous-champs",
"xpack.dataVisualizer.index.fieldNameDescription.numberField": "Valeurs Long, Entier, Court, Octet, Double et Élément flottant",
"xpack.dataVisualizer.index.fieldNameDescription.stringField": "Texte intégral tel que le corps d'un e-mail ou la description d'un produit",
"xpack.dataVisualizer.index.fieldNameDescription.textField": "Texte intégral tel que le corps d'un e-mail ou la description d'un produit",
"xpack.dataVisualizer.index.fieldNameDescription.unknownField": "Champ inconnu",
"xpack.dataVisualizer.index.fieldNameDescription.viewSupportedDateFormatsLinkText": "Affichez les formats de date pris en charge.",
"xpack.dataVisualizer.index.fieldNameSelect": "Nom du champ",
"xpack.dataVisualizer.index.fieldTypeSelect": "Type du champ",
"xpack.dataVisualizer.index.lensChart.countLabel": "Décompte",

View file

@ -10815,8 +10815,6 @@
"xpack.dataVisualizer.index.dataLoader.internalServerErrorMessage": "インデックス{index}のデータの読み込み中にエラーが発生。{message}。リクエストがタイムアウトした可能性があります。小さなサンプルサイズを使うか、時間範囲を狭めてみてください。",
"xpack.dataVisualizer.index.dataViewNotBasedOnTimeSeriesNotificationTitle": "データビュー{dataViewTitle}は時系列に基づいていません",
"xpack.dataVisualizer.index.errorLoadingDataMessage": "インデックス{index}のデータの読み込み中にエラーが発生。{message}。",
"xpack.dataVisualizer.index.fieldNameDescription.dateRangeField": "{dateFieldTypeLink}値の範囲。{viewSupportedDateFormatsLink}",
"xpack.dataVisualizer.index.fieldNameDescription.versionField": "ソフトウェアバージョン。{SemanticVersioningLink}事前ルールをサポートします",
"xpack.dataVisualizer.index.lensChart.averageOfLabel": "{fieldName} の平均",
"xpack.dataVisualizer.index.lensChart.chartTitle": "{fieldName}のLens",
"xpack.dataVisualizer.index.savedSearchErrorMessage": "保存された検索{savedSearchId}の取得エラー",
@ -10881,22 +10879,6 @@
"xpack.dataVisualizer.fieldStats.maxTitle": "最高",
"xpack.dataVisualizer.fieldStats.medianTitle": "中間",
"xpack.dataVisualizer.fieldStats.minTitle": "分",
"xpack.dataVisualizer.fieldTypeIcon.booleanTypeLabel": "ブール",
"xpack.dataVisualizer.fieldTypeIcon.conflictTypeLabel": "競合",
"xpack.dataVisualizer.fieldTypeIcon.dateRangeTypeLabel": "日付範囲",
"xpack.dataVisualizer.fieldTypeIcon.dateTypeLabel": "日付",
"xpack.dataVisualizer.fieldTypeIcon.geoPointTypeLabel": "地理ポイント",
"xpack.dataVisualizer.fieldTypeIcon.geoShapeTypeLabel": "地理情報図形",
"xpack.dataVisualizer.fieldTypeIcon.histogramTypeLabel": "ヒストグラム",
"xpack.dataVisualizer.fieldTypeIcon.ipRangeTypeLabel": "IP範囲",
"xpack.dataVisualizer.fieldTypeIcon.ipTypeLabel": "IP",
"xpack.dataVisualizer.fieldTypeIcon.keywordTypeLabel": "キーワード",
"xpack.dataVisualizer.fieldTypeIcon.murmur3TypeLabel": "Murmur3",
"xpack.dataVisualizer.fieldTypeIcon.numberTypeLabel": "数字",
"xpack.dataVisualizer.fieldTypeIcon.stringTypeLabel": "文字列",
"xpack.dataVisualizer.fieldTypeIcon.textTypeLabel": "テキスト",
"xpack.dataVisualizer.fieldTypeIcon.unknownTypeLabel": "不明",
"xpack.dataVisualizer.fieldTypeIcon.versionTypeLabel": "バージョン",
"xpack.dataVisualizer.fieldTypeSelect": "フィールド型",
"xpack.dataVisualizer.fieldTypesPopover.buttonAriaLabel": "フィルタータイプのヘルプ",
"xpack.dataVisualizer.fieldTypesPopover.dataTypeColumnTitle": "データ型",
@ -11040,7 +11022,6 @@
"xpack.dataVisualizer.index.actionsPanel.discoverAppTitle": "Discover",
"xpack.dataVisualizer.index.actionsPanel.exploreTitle": "データの調査",
"xpack.dataVisualizer.index.actionsPanel.viewIndexInDiscoverDescription": "インデックスのドキュメントを調査します。",
"xpack.dataVisualizer.index.advancedSettings.discover.fieldNameDescription.versionFieldLinkText": "セマンティックバージョニング",
"xpack.dataVisualizer.index.components.grid.description": "データの可視化",
"xpack.dataVisualizer.index.components.grid.displayName": "データビジュアライザーグリッド",
"xpack.dataVisualizer.index.dataGrid.actionsColumnLabel": "アクション",
@ -11061,23 +11042,6 @@
"xpack.dataVisualizer.index.embeddableErrorTitle": "埋め込み可能オブジェクトの読み込みエラー",
"xpack.dataVisualizer.index.embeddableNoResultsMessage": "結果が見つかりませんでした",
"xpack.dataVisualizer.index.errorFetchingFieldStatisticsMessage": "フィールド統計情報の取得エラー",
"xpack.dataVisualizer.index.fieldNameDescription.booleanField": "TrueおよびFalse値",
"xpack.dataVisualizer.index.fieldNameDescription.conflictField": "フィールドには異なる型の値があります。[管理 > データビュー]で解決してください。",
"xpack.dataVisualizer.index.fieldNameDescription.dateField": "日付文字列、または1/1/1970以降の秒またはミリ秒の数値",
"xpack.dataVisualizer.index.fieldNameDescription.dateRangeFieldLinkText": "日付",
"xpack.dataVisualizer.index.fieldNameDescription.geoPointField": "緯度および経度点",
"xpack.dataVisualizer.index.fieldNameDescription.geoShapeField": "多角形などの複雑な図形",
"xpack.dataVisualizer.index.fieldNameDescription.histogramField": "ヒストグラムの形式の集計された数値",
"xpack.dataVisualizer.index.fieldNameDescription.ipAddressField": "IPv4およびIPv6アドレス",
"xpack.dataVisualizer.index.fieldNameDescription.ipAddressRangeField": "IPv4またはIPv6または混合のアドレスをサポートするIP値の範囲",
"xpack.dataVisualizer.index.fieldNameDescription.keywordField": "ID、電子メールアドレス、ホスト名、ステータスコード、タグなどの構造化されたコンテンツ",
"xpack.dataVisualizer.index.fieldNameDescription.murmur3Field": "値のハッシュタグを計算して格納するフィールド",
"xpack.dataVisualizer.index.fieldNameDescription.nestedField": "サブフィールド間の関係を保持するJSONオブジェクト",
"xpack.dataVisualizer.index.fieldNameDescription.numberField": "長整数、整数、短整数、バイト、倍精度浮動小数点数、浮動小数点数の値",
"xpack.dataVisualizer.index.fieldNameDescription.stringField": "電子メール本文や製品説明などの全文テキスト",
"xpack.dataVisualizer.index.fieldNameDescription.textField": "電子メール本文や製品説明などの全文テキスト",
"xpack.dataVisualizer.index.fieldNameDescription.unknownField": "不明なフィールド",
"xpack.dataVisualizer.index.fieldNameDescription.viewSupportedDateFormatsLinkText": "サポートされている日付形式を表示します。",
"xpack.dataVisualizer.index.fieldNameSelect": "フィールド名",
"xpack.dataVisualizer.index.fieldTypeSelect": "フィールド型",
"xpack.dataVisualizer.index.lensChart.countLabel": "カウント",

View file

@ -10816,8 +10816,6 @@
"xpack.dataVisualizer.index.dataLoader.internalServerErrorMessage": "加载索引 {index} 中的数据时出错。{message}。请求可能已超时。请尝试使用较小的样例大小或缩小时间范围。",
"xpack.dataVisualizer.index.dataViewNotBasedOnTimeSeriesNotificationTitle": "数据视图 {dataViewTitle} 并非基于时间序列",
"xpack.dataVisualizer.index.errorLoadingDataMessage": "加载索引 {index} 中的数据时出错。{message}。",
"xpack.dataVisualizer.index.fieldNameDescription.dateRangeField": "{dateFieldTypeLink} 值的范围。{viewSupportedDateFormatsLink}",
"xpack.dataVisualizer.index.fieldNameDescription.versionField": "软件版本。支持 {SemanticVersioningLink} 优先规则",
"xpack.dataVisualizer.index.lensChart.averageOfLabel": "{fieldName} 的平均值",
"xpack.dataVisualizer.index.lensChart.chartTitle": "{fieldName} 的 Lens",
"xpack.dataVisualizer.index.savedSearchErrorMessage": "检索已保存搜索 {savedSearchId} 时出错",
@ -10882,22 +10880,6 @@
"xpack.dataVisualizer.fieldStats.maxTitle": "最大值",
"xpack.dataVisualizer.fieldStats.medianTitle": "中值",
"xpack.dataVisualizer.fieldStats.minTitle": "最小值",
"xpack.dataVisualizer.fieldTypeIcon.booleanTypeLabel": "布尔型",
"xpack.dataVisualizer.fieldTypeIcon.conflictTypeLabel": "冲突",
"xpack.dataVisualizer.fieldTypeIcon.dateRangeTypeLabel": "日期范围",
"xpack.dataVisualizer.fieldTypeIcon.dateTypeLabel": "日期",
"xpack.dataVisualizer.fieldTypeIcon.geoPointTypeLabel": "地理点",
"xpack.dataVisualizer.fieldTypeIcon.geoShapeTypeLabel": "几何形状",
"xpack.dataVisualizer.fieldTypeIcon.histogramTypeLabel": "直方图",
"xpack.dataVisualizer.fieldTypeIcon.ipRangeTypeLabel": "IP 范围",
"xpack.dataVisualizer.fieldTypeIcon.ipTypeLabel": "IP",
"xpack.dataVisualizer.fieldTypeIcon.keywordTypeLabel": "关键字",
"xpack.dataVisualizer.fieldTypeIcon.murmur3TypeLabel": "Murmur3",
"xpack.dataVisualizer.fieldTypeIcon.numberTypeLabel": "数字",
"xpack.dataVisualizer.fieldTypeIcon.stringTypeLabel": "字符串",
"xpack.dataVisualizer.fieldTypeIcon.textTypeLabel": "文本",
"xpack.dataVisualizer.fieldTypeIcon.unknownTypeLabel": "未知",
"xpack.dataVisualizer.fieldTypeIcon.versionTypeLabel": "版本",
"xpack.dataVisualizer.fieldTypeSelect": "字段类型",
"xpack.dataVisualizer.fieldTypesPopover.buttonAriaLabel": "筛选类型帮助",
"xpack.dataVisualizer.fieldTypesPopover.dataTypeColumnTitle": "数据类型",
@ -11041,7 +11023,6 @@
"xpack.dataVisualizer.index.actionsPanel.discoverAppTitle": "Discover",
"xpack.dataVisualizer.index.actionsPanel.exploreTitle": "浏览您的数据",
"xpack.dataVisualizer.index.actionsPanel.viewIndexInDiscoverDescription": "浏览您的索引中的文档。",
"xpack.dataVisualizer.index.advancedSettings.discover.fieldNameDescription.versionFieldLinkText": "语义版本控制",
"xpack.dataVisualizer.index.components.grid.description": "可视化数据",
"xpack.dataVisualizer.index.components.grid.displayName": "数据可视化工具网格",
"xpack.dataVisualizer.index.dataGrid.actionsColumnLabel": "操作",
@ -11062,23 +11043,6 @@
"xpack.dataVisualizer.index.embeddableErrorTitle": "加载可嵌入对象时出错",
"xpack.dataVisualizer.index.embeddableNoResultsMessage": "找不到结果",
"xpack.dataVisualizer.index.errorFetchingFieldStatisticsMessage": "提取字段统计信息时出错",
"xpack.dataVisualizer.index.fieldNameDescription.booleanField": "True 和 False 值",
"xpack.dataVisualizer.index.fieldNameDescription.conflictField": "字体具有不同类型的值。在“管理”>“数据视图”中解析。",
"xpack.dataVisualizer.index.fieldNameDescription.dateField": "日期字符串或 1/1/1970 以来的秒数或毫秒数",
"xpack.dataVisualizer.index.fieldNameDescription.dateRangeFieldLinkText": "日期",
"xpack.dataVisualizer.index.fieldNameDescription.geoPointField": "纬度和经度点",
"xpack.dataVisualizer.index.fieldNameDescription.geoShapeField": "复杂形状,如多边形",
"xpack.dataVisualizer.index.fieldNameDescription.histogramField": "直方图形式的预聚合数字值",
"xpack.dataVisualizer.index.fieldNameDescription.ipAddressField": "IPv4 和 IPv6 地址",
"xpack.dataVisualizer.index.fieldNameDescription.ipAddressRangeField": "支持 IPv4 或 IPv6或混合地址的 IP 值的范围",
"xpack.dataVisualizer.index.fieldNameDescription.keywordField": "结构化内容,如 ID、电子邮件地址、主机名、状态代码或标签",
"xpack.dataVisualizer.index.fieldNameDescription.murmur3Field": "计算和存储值哈希的字段",
"xpack.dataVisualizer.index.fieldNameDescription.nestedField": "保留其子字段之间关系的 JSON 对象",
"xpack.dataVisualizer.index.fieldNameDescription.numberField": "长整型、整数、短整型、字节、双精度和浮点值",
"xpack.dataVisualizer.index.fieldNameDescription.stringField": "全文本,如电子邮件正文或产品描述",
"xpack.dataVisualizer.index.fieldNameDescription.textField": "全文本,如电子邮件正文或产品描述",
"xpack.dataVisualizer.index.fieldNameDescription.unknownField": "未知字段",
"xpack.dataVisualizer.index.fieldNameDescription.viewSupportedDateFormatsLinkText": "查看支持的日期格式。",
"xpack.dataVisualizer.index.fieldNameSelect": "字段名称",
"xpack.dataVisualizer.index.fieldTypeSelect": "字段类型",
"xpack.dataVisualizer.index.lensChart.countLabel": "计数",

View file

@ -7,7 +7,7 @@
import type { FieldVisConfig } from '@kbn/data-visualizer-plugin/public/application/common/components/stats_table/types';
export interface MetricFieldVisConfig extends FieldVisConfig {
export interface MetricFieldVisConfig extends Omit<FieldVisConfig, 'secondaryType'> {
fieldName: string;
statsMaxDecimalPlaces: number;
docCountFormatted: string;
@ -16,7 +16,7 @@ export interface MetricFieldVisConfig extends FieldVisConfig {
hasActionMenu?: boolean;
}
export interface NonMetricFieldVisConfig extends FieldVisConfig {
export interface NonMetricFieldVisConfig extends Omit<FieldVisConfig, 'secondaryType'> {
fieldName: string;
docCountFormatted: string;
exampleCount: number;