mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] AIOps Fixing field aliases in pattern analysis (#176586)
This commit is contained in:
parent
2d36a14de9
commit
d809be71f0
6 changed files with 60 additions and 20 deletions
|
@ -50,7 +50,12 @@ export function processCategoryResults(
|
||||||
regex: b.regex,
|
regex: b.regex,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// check the first category for examples to determine if examples are available
|
||||||
|
const hasExamples = categories[0]?.examples.some((e) => e !== undefined);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
categories,
|
categories,
|
||||||
|
hasExamples,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
EuiHorizontalRule,
|
EuiHorizontalRule,
|
||||||
EuiSpacer,
|
EuiSpacer,
|
||||||
EuiButtonIcon,
|
EuiButtonIcon,
|
||||||
|
EuiToolTip,
|
||||||
|
EuiIcon,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
@ -42,7 +44,7 @@ import type { EventRate } from '../use_categorize_request';
|
||||||
import { getLabels } from './labels';
|
import { getLabels } from './labels';
|
||||||
import { TableHeader } from './table_header';
|
import { TableHeader } from './table_header';
|
||||||
import { ExpandedRow } from './expanded_row';
|
import { ExpandedRow } from './expanded_row';
|
||||||
import { FormattedPatternExamples } from '../format_category';
|
import { FormattedPatternExamples, FormattedTokens } from '../format_category';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
|
@ -60,6 +62,7 @@ interface Props {
|
||||||
enableRowActions?: boolean;
|
enableRowActions?: boolean;
|
||||||
additionalFilter?: CategorizationAdditionalFilter;
|
additionalFilter?: CategorizationAdditionalFilter;
|
||||||
navigateToDiscover?: boolean;
|
navigateToDiscover?: boolean;
|
||||||
|
displayExamples?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CategoryTable: FC<Props> = ({
|
export const CategoryTable: FC<Props> = ({
|
||||||
|
@ -78,6 +81,7 @@ export const CategoryTable: FC<Props> = ({
|
||||||
enableRowActions = true,
|
enableRowActions = true,
|
||||||
additionalFilter,
|
additionalFilter,
|
||||||
navigateToDiscover = true,
|
navigateToDiscover = true,
|
||||||
|
displayExamples = true,
|
||||||
}) => {
|
}) => {
|
||||||
const euiTheme = useEuiTheme();
|
const euiTheme = useEuiTheme();
|
||||||
const primaryBackgroundColor = useEuiBackgroundColor('primary');
|
const primaryBackgroundColor = useEuiBackgroundColor('primary');
|
||||||
|
@ -142,11 +146,13 @@ export const CategoryTable: FC<Props> = ({
|
||||||
if (itemIdToExpandedRowMapValues[category.key]) {
|
if (itemIdToExpandedRowMapValues[category.key]) {
|
||||||
delete itemIdToExpandedRowMapValues[category.key];
|
delete itemIdToExpandedRowMapValues[category.key];
|
||||||
} else {
|
} else {
|
||||||
itemIdToExpandedRowMapValues[category.key] = <ExpandedRow category={category} />;
|
itemIdToExpandedRowMapValues[category.key] = (
|
||||||
|
<ExpandedRow category={category} displayExamples={displayExamples} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
|
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
|
||||||
},
|
},
|
||||||
[itemIdToExpandedRowMap]
|
[displayExamples, itemIdToExpandedRowMap]
|
||||||
);
|
);
|
||||||
|
|
||||||
const columns: Array<EuiBasicTableColumn<Category>> = [
|
const columns: Array<EuiBasicTableColumn<Category>> = [
|
||||||
|
@ -185,11 +191,7 @@ export const CategoryTable: FC<Props> = ({
|
||||||
defaultMessage: 'Examples',
|
defaultMessage: 'Examples',
|
||||||
}),
|
}),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: (item: Category) => (
|
render: (item: Category) => <FormattedPatternExamples category={item} count={1} />,
|
||||||
<>
|
|
||||||
<FormattedPatternExamples category={item} count={1} />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n.translate('xpack.aiops.logCategorization.column.actions', {
|
name: i18n.translate('xpack.aiops.logCategorization.column.actions', {
|
||||||
|
@ -218,6 +220,29 @@ export const CategoryTable: FC<Props> = ({
|
||||||
},
|
},
|
||||||
] as Array<EuiBasicTableColumn<Category>>;
|
] as Array<EuiBasicTableColumn<Category>>;
|
||||||
|
|
||||||
|
if (displayExamples === false) {
|
||||||
|
// on the rare occasion that examples are not available, replace the examples column with tokens
|
||||||
|
columns.splice(2, 1, {
|
||||||
|
name: (
|
||||||
|
<EuiToolTip
|
||||||
|
position="top"
|
||||||
|
content={i18n.translate('xpack.aiops.logCategorization.column.tokens.tooltip', {
|
||||||
|
defaultMessage:
|
||||||
|
'If the selected field is an alias, example documents cannot be displayed. Showing pattern tokens instead.',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
{i18n.translate('xpack.aiops.logCategorization.column.tokens', {
|
||||||
|
defaultMessage: 'Tokens',
|
||||||
|
})}
|
||||||
|
<EuiIcon size="s" color="subdued" type="questionInCircle" className="eui-alignTop" />
|
||||||
|
</>
|
||||||
|
</EuiToolTip>
|
||||||
|
),
|
||||||
|
render: (item: Category) => <FormattedTokens category={item} count={1} />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (showSparkline === true) {
|
if (showSparkline === true) {
|
||||||
columns.splice(2, 0, {
|
columns.splice(2, 0, {
|
||||||
field: 'sparkline',
|
field: 'sparkline',
|
||||||
|
|
|
@ -15,9 +15,10 @@ import { FormattedPatternExamples, FormattedRegex, FormattedTokens } from '../fo
|
||||||
|
|
||||||
interface ExpandedRowProps {
|
interface ExpandedRowProps {
|
||||||
category: Category;
|
category: Category;
|
||||||
|
displayExamples?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExpandedRow: FC<ExpandedRowProps> = ({ category }) => {
|
export const ExpandedRow: FC<ExpandedRowProps> = ({ category, displayExamples = true }) => {
|
||||||
const { euiTheme } = useEuiTheme();
|
const { euiTheme } = useEuiTheme();
|
||||||
const cssExpandedRow = css({
|
const cssExpandedRow = css({
|
||||||
marginRight: euiTheme.size.xxl,
|
marginRight: euiTheme.size.xxl,
|
||||||
|
@ -44,13 +45,15 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ category }) => {
|
||||||
<FormattedRegex category={category} />
|
<FormattedRegex category={category} />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section
|
{displayExamples ? (
|
||||||
title={i18n.translate('xpack.aiops.logCategorization.expandedRow.title.examples', {
|
<Section
|
||||||
defaultMessage: 'Examples',
|
title={i18n.translate('xpack.aiops.logCategorization.expandedRow.title.examples', {
|
||||||
})}
|
defaultMessage: 'Examples',
|
||||||
>
|
})}
|
||||||
<FormattedPatternExamples category={category} />
|
>
|
||||||
</Section>
|
<FormattedPatternExamples category={category} />
|
||||||
|
</Section>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -110,6 +110,7 @@ export const LogCategorizationFlyout: FC<LogCategorizationPageProps> = ({
|
||||||
const [data, setData] = useState<{
|
const [data, setData] = useState<{
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
categoriesInBucket: Category[] | null;
|
categoriesInBucket: Category[] | null;
|
||||||
|
displayExamples: boolean;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [fieldValidationResult, setFieldValidationResult] = useState<FieldValidationResults | null>(
|
const [fieldValidationResult, setFieldValidationResult] = useState<FieldValidationResults | null>(
|
||||||
null
|
null
|
||||||
|
@ -191,7 +192,7 @@ export const LogCategorizationFlyout: FC<LogCategorizationPageProps> = ({
|
||||||
|
|
||||||
if (mounted.current === true) {
|
if (mounted.current === true) {
|
||||||
setFieldValidationResult(validationResult);
|
setFieldValidationResult(validationResult);
|
||||||
const { categories } = categorizationResult;
|
const { categories, hasExamples } = categorizationResult;
|
||||||
|
|
||||||
const hasBucketCategories = categories.some((c) => c.subTimeRangeCount !== undefined);
|
const hasBucketCategories = categories.some((c) => c.subTimeRangeCount !== undefined);
|
||||||
let categoriesInBucket: any | null = null;
|
let categoriesInBucket: any | null = null;
|
||||||
|
@ -210,6 +211,7 @@ export const LogCategorizationFlyout: FC<LogCategorizationPageProps> = ({
|
||||||
setData({
|
setData({
|
||||||
categories,
|
categories,
|
||||||
categoriesInBucket,
|
categoriesInBucket,
|
||||||
|
displayExamples: hasExamples,
|
||||||
});
|
});
|
||||||
|
|
||||||
setShowTabs(hasBucketCategories);
|
setShowTabs(hasBucketCategories);
|
||||||
|
@ -388,6 +390,7 @@ export const LogCategorizationFlyout: FC<LogCategorizationPageProps> = ({
|
||||||
<EuiSpacer size="s" />
|
<EuiSpacer size="s" />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<CategoryTable
|
<CategoryTable
|
||||||
categories={
|
categories={
|
||||||
selectedTab === SELECTED_TAB.BUCKET && data.categoriesInBucket !== null
|
selectedTab === SELECTED_TAB.BUCKET && data.categoriesInBucket !== null
|
||||||
|
@ -412,6 +415,7 @@ export const LogCategorizationFlyout: FC<LogCategorizationPageProps> = ({
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
navigateToDiscover={additionalFilter !== undefined}
|
navigateToDiscover={additionalFilter !== undefined}
|
||||||
|
displayExamples={data.displayExamples}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -89,6 +89,7 @@ export const LogCategorizationPage: FC<LogCategorizationPageProps> = ({ embeddin
|
||||||
const [pinnedCategory, setPinnedCategory] = useState<Category | null>(null);
|
const [pinnedCategory, setPinnedCategory] = useState<Category | null>(null);
|
||||||
const [data, setData] = useState<{
|
const [data, setData] = useState<{
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
|
displayExamples: boolean;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [fieldValidationResult, setFieldValidationResult] = useState<FieldValidationResults | null>(
|
const [fieldValidationResult, setFieldValidationResult] = useState<FieldValidationResults | null>(
|
||||||
null
|
null
|
||||||
|
@ -212,6 +213,7 @@ export const LogCategorizationPage: FC<LogCategorizationPageProps> = ({ embeddin
|
||||||
setFieldValidationResult(validationResult);
|
setFieldValidationResult(validationResult);
|
||||||
setData({
|
setData({
|
||||||
categories: categorizationResult.categories,
|
categories: categorizationResult.categories,
|
||||||
|
displayExamples: categorizationResult.hasExamples,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toasts.addError(error, {
|
toasts.addError(error, {
|
||||||
|
@ -401,6 +403,7 @@ export const LogCategorizationPage: FC<LogCategorizationPageProps> = ({ embeddin
|
||||||
selectedCategory={selectedCategory}
|
selectedCategory={selectedCategory}
|
||||||
setSelectedCategory={setSelectedCategory}
|
setSelectedCategory={setSelectedCategory}
|
||||||
timefilter={timefilter}
|
timefilter={timefilter}
|
||||||
|
displayExamples={data.displayExamples}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</EuiPageBody>
|
</EuiPageBody>
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
createCategoryRequest,
|
createCategoryRequest,
|
||||||
} from '../../../common/api/log_categorization/create_category_request';
|
} from '../../../common/api/log_categorization/create_category_request';
|
||||||
import { processCategoryResults } from '../../../common/api/log_categorization/process_category_results';
|
import { processCategoryResults } from '../../../common/api/log_categorization/process_category_results';
|
||||||
import type { Category, CatResponse } from '../../../common/api/log_categorization/types';
|
import type { CatResponse } from '../../../common/api/log_categorization/types';
|
||||||
|
|
||||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||||
import {
|
import {
|
||||||
|
@ -71,7 +71,7 @@ export function useCategorizeRequest() {
|
||||||
query: QueryDslQueryContainer,
|
query: QueryDslQueryContainer,
|
||||||
intervalMs?: number,
|
intervalMs?: number,
|
||||||
additionalFilter?: CategorizationAdditionalFilter
|
additionalFilter?: CategorizationAdditionalFilter
|
||||||
): Promise<{ categories: Category[] }> => {
|
): Promise<ReturnType<typeof processCategoryResults>> => {
|
||||||
const { wrap, unwrap } = randomSampler.createRandomSamplerWrapper();
|
const { wrap, unwrap } = randomSampler.createRandomSamplerWrapper();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -101,7 +101,7 @@ export function useCategorizeRequest() {
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
if (error.name === 'AbortError') {
|
if (error.name === 'AbortError') {
|
||||||
return resolve({ categories: [] });
|
return resolve({ categories: [], hasExamples: false });
|
||||||
}
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue