[ML] AIOps Fixing field aliases in pattern analysis (#176586)

This commit is contained in:
James Gowdy 2024-02-13 20:32:31 +00:00 committed by GitHub
parent 2d36a14de9
commit d809be71f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 60 additions and 20 deletions

View file

@ -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,
}; };
} }

View file

@ -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',

View file

@ -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,6 +45,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ category }) => {
<FormattedRegex category={category} /> <FormattedRegex category={category} />
</Section> </Section>
{displayExamples ? (
<Section <Section
title={i18n.translate('xpack.aiops.logCategorization.expandedRow.title.examples', { title={i18n.translate('xpack.aiops.logCategorization.expandedRow.title.examples', {
defaultMessage: 'Examples', defaultMessage: 'Examples',
@ -51,6 +53,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ category }) => {
> >
<FormattedPatternExamples category={category} /> <FormattedPatternExamples category={category} />
</Section> </Section>
) : null}
</div> </div>
); );
}; };

View file

@ -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}

View file

@ -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>

View file

@ -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);
}, },