Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[App Search] Added a query performance rating to the Result Settings page #96230

Merged
merged 4 commits into from
Apr 7, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoa TIL 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]}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweeeeet!

{badgeText[performanceRating]}
</EuiBadge>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,47 @@

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

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

import React from 'react';

import { shallow } from 'enzyme';

import { QueryPerformance } from './query_performance';
import { ResultSettings } from './result_settings';
import { ResultSettingsTable } from './result_settings_table';

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(QueryPerformance).exists()).toBe(true);
});

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

it('does not show a query performance rating when data is still loading', () => {
setMockValues({
dataLoading: true,
});
const wrapper = shallow(<ResultSettings engineBreadcrumb={['test']} />);
expect(wrapper.find(QueryPerformance).exists()).toBe(false);
});
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +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 { RESULT_SETTINGS_TITLE } from './constants';
import { QueryPerformance } from './query_performance';
import { ResultSettingsTable } from './result_settings_table';

import { ResultSettingsLogic } from '.';
Expand All @@ -24,6 +25,7 @@ interface Props {
}

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

useEffect(() => {
Expand All @@ -40,7 +42,7 @@ export const ResultSettings: React.FC<Props> = ({ engineBreadcrumb }) => {
<ResultSettingsTable />
</EuiFlexItem>
<EuiFlexItem grow={3}>
<div>TODO</div>
<div>{!dataLoading && <QueryPerformance />}</div>
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
</EuiFlexItem>
</EuiFlexGroup>
</>
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
JasonStoltz marked this conversation as resolved.
Show resolved Hide resolved
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);
JasonStoltz marked this conversation as resolved.
Show resolved Hide resolved
},
],
}),
listeners: ({ actions, values }) => ({
clearRawSizeForField: ({ fieldName }) => {
Expand Down