Skip to content

Commit f47749c

Browse files
committed
Normalize boost data
1 parent d04c143 commit f47749c

File tree

5 files changed

+166
-19
lines changed

5 files changed

+166
-19
lines changed

x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,25 @@ describe('RelevanceTuningLogic', () => {
256256
});
257257

258258
describe('initializeRelevanceTuning', () => {
259-
it('should make an API call and set state based on the response', async () => {
259+
it('should make an API call and set state based on the normalized response', async () => {
260260
mount();
261-
http.get.mockReturnValueOnce(Promise.resolve(relevanceTuningProps));
261+
http.get.mockReturnValueOnce(
262+
Promise.resolve({
263+
...relevanceTuningProps,
264+
searchSettings: {
265+
...relevanceTuningProps.searchSettings,
266+
boosts: {
267+
foo: [
268+
{
269+
type: 'value' as BoostType,
270+
factor: 5,
271+
value: 5,
272+
},
273+
],
274+
},
275+
},
276+
})
277+
);
262278
jest.spyOn(RelevanceTuningLogic.actions, 'onInitializeRelevanceTuning');
263279

264280
RelevanceTuningLogic.actions.initializeRelevanceTuning();
@@ -267,9 +283,21 @@ describe('RelevanceTuningLogic', () => {
267283
expect(http.get).toHaveBeenCalledWith(
268284
'/api/app_search/engines/test-engine/search_settings/details'
269285
);
270-
expect(RelevanceTuningLogic.actions.onInitializeRelevanceTuning).toHaveBeenCalledWith(
271-
relevanceTuningProps
272-
);
286+
expect(RelevanceTuningLogic.actions.onInitializeRelevanceTuning).toHaveBeenCalledWith({
287+
...relevanceTuningProps,
288+
searchSettings: {
289+
...relevanceTuningProps.searchSettings,
290+
boosts: {
291+
foo: [
292+
{
293+
type: 'value' as BoostType,
294+
factor: 5,
295+
value: ['5'],
296+
},
297+
],
298+
},
299+
},
300+
});
273301
});
274302

275303
it('handles errors', async () => {

x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,26 @@
88
import { kea, MakeLogicType } from 'kea';
99
import { omit, cloneDeep, isEmpty } from 'lodash';
1010

11+
import { setSuccessMessage, flashAPIErrors } from '../../../shared/flash_messages';
1112
import { HttpLogic } from '../../../shared/http';
1213
import { Schema, SchemaConflicts } from '../../../shared/types';
13-
import { setSuccessMessage, flashAPIErrors } from '../../../shared/flash_messages';
1414

15-
import { Result } from '../result/types';
1615
import { EngineLogic } from '../engine';
16+
import { Result } from '../result/types';
1717

18-
import { BaseBoost, Boost, BoostType, SearchSettings } from './types';
1918
import {
2019
UPDATE_SUCCESS_MESSAGE,
2120
RESET_CONFIRMATION_MESSAGE,
2221
DELETE_SUCCESS_MESSAGE,
2322
DELETE_CONFIRMATION_MESSAGE,
2423
} from './constants';
25-
import { filterIfTerm, parseBoostCenter, removeBoostStateProps } from './utils';
24+
import { BaseBoost, Boost, BoostType, SearchSettings } from './types';
25+
import {
26+
filterIfTerm,
27+
parseBoostCenter,
28+
removeBoostStateProps,
29+
normalizeBoostValues,
30+
} from './utils';
2631

2732
interface RelevanceTuningProps {
2833
searchSettings: SearchSettings;
@@ -60,8 +65,8 @@ interface RelevanceTuningActions {
6065
name: string,
6166
boostIndex: number,
6267
valueIndex: number,
63-
value: string | number
64-
): { name: string; boostIndex: number; valueIndex: number; value: string | number };
68+
value: string
69+
): { name: string; boostIndex: number; valueIndex: number; value: string };
6570
updateBoostCenter(
6671
name: string,
6772
boostIndex: number,
@@ -252,7 +257,13 @@ export const RelevanceTuningLogic = kea<
252257

253258
try {
254259
const response = await http.get(url);
255-
actions.onInitializeRelevanceTuning(response);
260+
actions.onInitializeRelevanceTuning({
261+
...response,
262+
searchSettings: {
263+
...response.searchSettings,
264+
boosts: normalizeBoostValues(response.searchSettings.boosts),
265+
},
266+
});
256267
} catch (e) {
257268
flashAPIErrors(e);
258269
}

x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/types.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@ export interface BaseBoost {
1212
function?: string;
1313
}
1414

15-
export interface Boost extends BaseBoost {
15+
// A boost that comes from the server, before we normalize it has a much looser schema
16+
export interface RawBoost extends BaseBoost {
1617
type: BoostType;
1718
newBoost?: boolean;
1819
center?: string | number;
19-
value?: Array<string | number>;
20+
value?: string | number | boolean | object | Array<string | number | boolean | object>;
2021
factor: number;
2122
}
2223

23-
export interface BoostObject {
24-
[key: string]: Boost[];
24+
// We normalize raw boosts to make them safer and easier to work with
25+
export interface Boost extends RawBoost {
26+
value?: string[];
2527
}
26-
2728
export interface SearchSettings {
2829
boosts: Record<string, Boost[]>;
2930
search_fields: Record<
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
import { BoostType } from './types';
8+
import { normalizeBoostValues } from './utils';
9+
10+
describe('normalizeBoostValues', () => {
11+
const boosts = {
12+
foo: [
13+
{
14+
type: 'value' as BoostType,
15+
factor: 9.5,
16+
value: 1,
17+
},
18+
{
19+
type: 'value' as BoostType,
20+
factor: 9.5,
21+
value: '1',
22+
},
23+
{
24+
type: 'value' as BoostType,
25+
factor: 9.5,
26+
value: [1],
27+
},
28+
{
29+
type: 'value' as BoostType,
30+
factor: 9.5,
31+
value: ['1'],
32+
},
33+
{
34+
type: 'value' as BoostType,
35+
factor: 9.5,
36+
value: [
37+
'1',
38+
1,
39+
'2',
40+
2,
41+
true,
42+
{
43+
b: 'a',
44+
},
45+
{},
46+
],
47+
},
48+
],
49+
bar: [
50+
{
51+
type: 'proximity' as BoostType,
52+
factor: 9.5,
53+
},
54+
],
55+
sp_def: [
56+
{
57+
type: 'functional' as BoostType,
58+
factor: 5,
59+
},
60+
],
61+
};
62+
63+
it('renders', () => {
64+
expect(normalizeBoostValues(boosts)).toEqual({
65+
bar: [{ factor: 9.5, type: 'proximity' }],
66+
foo: [
67+
{ factor: 9.5, type: 'value', value: ['1'] },
68+
{ factor: 9.5, type: 'value', value: ['1'] },
69+
{ factor: 9.5, type: 'value', value: ['1'] },
70+
{ factor: 9.5, type: 'value', value: ['1'] },
71+
{
72+
factor: 9.5,
73+
type: 'value',
74+
value: ['1', '1', '2', '2', 'true', '[object Object]', '[object Object]'],
75+
},
76+
],
77+
sp_def: [{ type: 'functional', factor: 5 }],
78+
});
79+
});
80+
});

x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
* 2.0.
66
*/
77

8-
import { cloneDeep } from 'lodash';
8+
import { cloneDeep, omit } from 'lodash';
99

1010
import { NUMBER } from '../../../shared/constants/field_types';
1111
import { SchemaTypes } from '../../../shared/types';
1212

13-
import { SearchSettings } from './types';
13+
import { RawBoost, Boost, SearchSettings, BoostType } from './types';
1414

1515
// If the user hasn't entered a filter, then we can skip filtering the array entirely
1616
export const filterIfTerm = (array: string[], filterTerm: string): string[] => {
@@ -34,3 +34,30 @@ export const parseBoostCenter = (fieldType: SchemaTypes, value: string | number)
3434
}
3535
return value;
3636
};
37+
38+
const toArray = <T>(v: T | T[]): T[] => (Array.isArray(v) ? v : [v]);
39+
const toString = <T>(v1: T) => String(v1);
40+
41+
const normalizeBoostValue = (boost: RawBoost): Boost => {
42+
if (!boost.hasOwnProperty('value')) {
43+
// Can't simply do `return boost` here as TS can't infer the correct type
44+
return omit(boost, 'value');
45+
}
46+
47+
return {
48+
...boost,
49+
type: boost.type as BoostType,
50+
value: toArray(boost.value).map(toString),
51+
};
52+
};
53+
54+
// Data may have been set to invalid types directly via the public App Search API. Since these are invalid, we don't want to deal
55+
// with them as valid types in the UI. For that reason, we stringify all values here, as the data comes in.
56+
// Additionally, values can be in single values or in arrays.
57+
export const normalizeBoostValues = (boosts: Record<string, RawBoost[]>): Record<string, Boost[]> =>
58+
Object.entries(boosts).reduce((newBoosts, [fieldName, boostList]) => {
59+
return {
60+
...newBoosts,
61+
[fieldName]: boostList.map(normalizeBoostValue),
62+
};
63+
}, {});

0 commit comments

Comments
 (0)