Skip to content

Commit

Permalink
[App Search] Added a query performance rating to the Result Settings …
Browse files Browse the repository at this point in the history
…page (#96230) (#96449)

Co-authored-by: Jason Stoltzfus <[email protected]>
  • Loading branch information
kibanamachine and JasonStoltz authored Apr 7, 2021
1 parent aedd603 commit 3726c7b
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { QueryPerformance } from './query_performance';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 { setMockValues } from '../../../../__mocks__/kea.mock';

import React from 'react';

import { shallow } from 'enzyme';

import { EuiBadge } from '@elastic/eui';

import { QueryPerformance } from './query_performance';

describe('QueryPerformance', () => {
const values = {
queryPerformanceScore: 1,
};

beforeEach(() => {
jest.clearAllMocks();
setMockValues(values);
});

it('renders as green with the text "optimal" for a performance score of less than 6', () => {
const wrapper = shallow(<QueryPerformance />);
expect(wrapper.find(EuiBadge).prop('color')).toEqual('#59deb4');
expect(wrapper.find(EuiBadge).children().text()).toEqual('Query performance: optimal');
});

it('renders as blue with the text "good" for a performance score of less than 11', () => {
setMockValues({
queryPerformanceScore: 10,
});
const wrapper = shallow(<QueryPerformance />);
expect(wrapper.find(EuiBadge).prop('color')).toEqual('#40bfff');
expect(wrapper.find(EuiBadge).children().text()).toEqual('Query performance: good');
});

it('renders as yellow with the text "standard" for a performance score of less than 21', () => {
setMockValues({
queryPerformanceScore: 20,
});
const wrapper = shallow(<QueryPerformance />);
expect(wrapper.find(EuiBadge).prop('color')).toEqual('#fed566');
expect(wrapper.find(EuiBadge).children().text()).toEqual('Query performance: standard');
});

it('renders as red with the text "delayed" for a performance score of 21 or more', () => {
setMockValues({
queryPerformanceScore: 100,
});
const wrapper = shallow(<QueryPerformance />);
expect(wrapper.find(EuiBadge).prop('color')).toEqual('#ff9173');
expect(wrapper.find(EuiBadge).children().text()).toEqual('Query performance: delayed');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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 React from 'react';

import { useValues } from 'kea';

import { EuiBadge } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { ResultSettingsLogic } from '../result_settings_logic';

enum QueryPerformanceRating {
Optimal = 'Optimal',
Good = 'Good',
Standard = 'Standard',
Delayed = 'Delayed',
}

const QUERY_PERFORMANCE_LABEL = (performanceValue: string) =>
i18n.translate('xpack.enterpriseSearch.appSearch.engine.resultSettings.queryPerformanceLabel', {
defaultMessage: 'Query performance: {performanceValue}',
values: {
performanceValue,
},
});

const QUERY_PERFORMANCE_OPTIMAL = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.queryPerformance.optimalValue',
{ defaultMessage: 'optimal' }
);

const QUERY_PERFORMANCE_GOOD = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.queryPerformance.goodValue',
{ defaultMessage: 'good' }
);

const QUERY_PERFORMANCE_STANDARD = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.queryPerformance.standardValue',
{ defaultMessage: 'standard' }
);

const QUERY_PERFORMANCE_DELAYED = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.queryPerformance.delayedValue',
{ defaultMessage: 'delayed' }
);

const badgeText: Record<QueryPerformanceRating, string> = {
[QueryPerformanceRating.Optimal]: QUERY_PERFORMANCE_LABEL(QUERY_PERFORMANCE_OPTIMAL),
[QueryPerformanceRating.Good]: QUERY_PERFORMANCE_LABEL(QUERY_PERFORMANCE_GOOD),
[QueryPerformanceRating.Standard]: QUERY_PERFORMANCE_LABEL(QUERY_PERFORMANCE_STANDARD),
[QueryPerformanceRating.Delayed]: QUERY_PERFORMANCE_LABEL(QUERY_PERFORMANCE_DELAYED),
};

const badgeColors: Record<QueryPerformanceRating, string> = {
[QueryPerformanceRating.Optimal]: '#59deb4',
[QueryPerformanceRating.Good]: '#40bfff',
[QueryPerformanceRating.Standard]: '#fed566',
[QueryPerformanceRating.Delayed]: '#ff9173',
};

const getPerformanceRating = (score: number) => {
switch (true) {
case score < 6:
return QueryPerformanceRating.Optimal;
case score < 11:
return QueryPerformanceRating.Good;
case score < 21:
return QueryPerformanceRating.Standard;
default:
return QueryPerformanceRating.Delayed;
}
};

export const QueryPerformance: React.FC = () => {
const { queryPerformanceScore } = useValues(ResultSettingsLogic);
const performanceRating = getPerformanceRating(queryPerformanceScore);
return (
<EuiBadge role="region" aria-live="polite" color={badgeColors[performanceRating]}>
{badgeText[performanceRating]}
</EuiBadge>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,48 @@

import '../../../__mocks__/shallow_useeffect.mock';

import { setMockActions } from '../../../__mocks__';
import { setMockValues, setMockActions } from '../../../__mocks__';

import React from 'react';

import { shallow } from 'enzyme';

import { ResultSettings } from './result_settings';
import { ResultSettingsTable } from './result_settings_table';
import { SampleResponse } from './sample_response';

describe('RelevanceTuning', () => {
const values = {
dataLoading: false,
};

const actions = {
initializeResultSettingsData: jest.fn(),
};

beforeEach(() => {
setMockValues(values);
setMockActions(actions);
jest.clearAllMocks();
});

it('renders', () => {
const wrapper = shallow(<ResultSettings engineBreadcrumb={['test']} />);
expect(wrapper.find(ResultSettingsTable).exists()).toBe(true);
expect(wrapper.find(SampleResponse).exists()).toBe(true);
});

it('initializes result settings data when mounted', () => {
shallow(<ResultSettings engineBreadcrumb={['test']} />);
expect(actions.initializeResultSettingsData).toHaveBeenCalled();
});

it('renders a loading screen if data has not loaded yet', () => {
setMockValues({
dataLoading: true,
});
const wrapper = shallow(<ResultSettings engineBreadcrumb={['test']} />);
expect(wrapper.find(ResultSettingsTable).exists()).toBe(false);
expect(wrapper.find(SampleResponse).exists()).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

import React, { useEffect } from 'react';

import { useActions } from 'kea';
import { useActions, useValues } from 'kea';

import { EuiPageHeader, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';

import { FlashMessages } from '../../../shared/flash_messages';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';

import { Loading } from '../../../shared/loading';

import { RESULT_SETTINGS_TITLE } from './constants';
import { ResultSettingsTable } from './result_settings_table';

Expand All @@ -26,12 +28,15 @@ interface Props {
}

export const ResultSettings: React.FC<Props> = ({ engineBreadcrumb }) => {
const { dataLoading } = useValues(ResultSettingsLogic);
const { initializeResultSettingsData } = useActions(ResultSettingsLogic);

useEffect(() => {
initializeResultSettingsData();
}, []);

if (dataLoading) return <Loading />;

return (
<>
<SetPageChrome trail={[...engineBreadcrumb, RESULT_SETTINGS_TITLE]} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('ResultSettingsLogic', () => {
stagedUpdates: false,
nonTextResultFields: {},
textResultFields: {},
queryPerformanceScore: 0,
};

// Values without selectors
Expand Down Expand Up @@ -487,6 +488,76 @@ describe('ResultSettingsLogic', () => {
});
});
});

describe('queryPerformanceScore', () => {
describe('returns a score for the current query performance based on the result settings', () => {
it('considers a text value with raw set (but no size) as worth 1.5', () => {
mount({
resultFields: { foo: { raw: true } },
schema: { foo: 'text' as SchemaTypes },
});
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(1.5);
});

it('considers a text value with raw set and a size over 250 as also worth 1.5', () => {
mount({
resultFields: { foo: { raw: true, rawSize: 251 } },
schema: { foo: 'text' as SchemaTypes },
});
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(1.5);
});

it('considers a text value with raw set and a size less than or equal to 250 as worth 1', () => {
mount({
resultFields: { foo: { raw: true, rawSize: 250 } },
schema: { foo: 'text' as SchemaTypes },
});
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(1);
});

it('considers a text value with a snippet set as worth 2', () => {
mount({
resultFields: { foo: { snippet: true, snippetSize: 50, snippetFallback: true } },
schema: { foo: 'text' as SchemaTypes },
});
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(2);
});

it('will sum raw and snippet values if both are set', () => {
mount({
resultFields: { foo: { snippet: true, raw: true } },
schema: { foo: 'text' as SchemaTypes },
});
// 1.5 (raw) + 2 (snippet) = 3.5
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(3.5);
});

it('considers a non-text value with raw set as 0.2', () => {
mount({
resultFields: { foo: { raw: true } },
schema: { foo: 'number' as SchemaTypes },
});
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(0.2);
});

it('can sum variations of all the prior', () => {
mount({
resultFields: {
foo: { raw: true },
bar: { raw: true, snippet: true },
baz: { raw: true },
},
schema: {
foo: 'text' as SchemaTypes,
bar: 'text' as SchemaTypes,
baz: 'number' as SchemaTypes,
},
});
// 1.5 (foo) + 3.5 (bar) + baz (.2) = 5.2
expect(ResultSettingsLogic.values.queryPerformanceScore).toEqual(5.2);
});
});
});
});

describe('listeners', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,19 @@ interface ResultSettingsValues {
dataLoading: boolean;
saving: boolean;
openModal: OpenModal;
nonTextResultFields: FieldResultSettingObject;
textResultFields: FieldResultSettingObject;
resultFields: FieldResultSettingObject;
serverResultFields: ServerFieldResultSettingObject;
lastSavedResultFields: FieldResultSettingObject;
schema: Schema;
schemaConflicts: SchemaConflicts;
// Selectors
textResultFields: FieldResultSettingObject;
nonTextResultFields: FieldResultSettingObject;
serverResultFields: ServerFieldResultSettingObject;
resultFieldsAtDefaultSettings: boolean;
resultFieldsEmpty: boolean;
stagedUpdates: true;
reducedServerResultFields: ServerFieldResultSettingObject;
queryPerformanceScore: number;
}

export const ResultSettingsLogic = kea<MakeLogicType<ResultSettingsValues, ResultSettingsActions>>({
Expand Down Expand Up @@ -221,6 +222,31 @@ export const ResultSettingsLogic = kea<MakeLogicType<ResultSettingsValues, Resul
{}
),
],
queryPerformanceScore: [
() => [selectors.serverResultFields, selectors.schema],
(serverResultFields: ServerFieldResultSettingObject, schema: Schema) => {
return Object.entries(serverResultFields).reduce((acc, [fieldName, resultField]) => {
let newAcc = acc;
if (resultField.raw) {
if (schema[fieldName] !== 'text') {
newAcc += 0.2;
} else if (
typeof resultField.raw === 'object' &&
resultField.raw.size &&
resultField.raw.size <= 250
) {
newAcc += 1.0;
} else {
newAcc += 1.5;
}
}
if (resultField.snippet) {
newAcc += 2.0;
}
return newAcc;
}, 0);
},
],
}),
listeners: ({ actions, values }) => ({
clearRawSizeForField: ({ fieldName }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { QueryPerformance } from '../query_performance';
import { ResultSettingsLogic } from '../result_settings_logic';

import { SampleResponseLogic } from './sample_response_logic';
Expand Down Expand Up @@ -48,7 +49,7 @@ export const SampleResponse: React.FC = () => {
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{/* TODO <QueryPerformance queryPerformanceRating={queryPerformanceRating} /> */}
<QueryPerformance />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
Expand Down

0 comments on commit 3726c7b

Please sign in to comment.