Skip to content

Commit 9cd38c7

Browse files
authored
Multi term search sort by search score (home-assistant#28353)
1 parent 6322c19 commit 9cd38c7

24 files changed

+685
-589
lines changed

src/components/data-table/ha-data-table.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import memoizeOne from "memoize-one";
1616
import { restoreScroll } from "../../common/decorators/restore-scroll";
1717
import { fireEvent } from "../../common/dom/fire_event";
1818
import { stringCompare } from "../../common/string/compare";
19+
import type { LocalizeFunc } from "../../common/translations/localize";
1920
import { debounce } from "../../common/util/debounce";
2021
import { groupBy } from "../../common/util/group-by";
22+
import { nextRender } from "../../common/util/render-status";
2123
import { haStyleScrollbar } from "../../resources/styles";
2224
import { loadVirtualizer } from "../../resources/virtualizer";
2325
import type { HomeAssistant } from "../../types";
@@ -26,8 +28,6 @@ import type { HaCheckbox } from "../ha-checkbox";
2628
import "../ha-svg-icon";
2729
import "../search-input";
2830
import { filterData, sortData } from "./sort-filter";
29-
import type { LocalizeFunc } from "../../common/translations/localize";
30-
import { nextRender } from "../../common/util/render-status";
3131

3232
export interface RowClickedEvent {
3333
id: string;

src/components/data-table/sort-filter-worker.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import { expose } from "comlink";
2-
import Fuse from "fuse.js";
2+
import Fuse, { type FuseOptionKey } from "fuse.js";
33
import memoizeOne from "memoize-one";
44
import { ipCompare, stringCompare } from "../../common/string/compare";
55
import { stripDiacritics } from "../../common/string/strip-diacritics";
6-
import { HaFuse } from "../../resources/fuse";
6+
import { multiTermSearch } from "../../resources/fuseMultiTerm";
77
import type {
88
ClonedDataTableColumnData,
99
DataTableRowData,
1010
SortableColumnContainer,
1111
SortingDirection,
1212
} from "./ha-data-table";
1313

14-
const fuseIndex = memoizeOne(
15-
(data: DataTableRowData[], columns: SortableColumnContainer) => {
14+
const getSearchKeys = memoizeOne(
15+
(columns: SortableColumnContainer): FuseOptionKey<DataTableRowData>[] => {
1616
const searchKeys = new Set<string>();
17+
1718
Object.entries(columns).forEach(([key, column]) => {
1819
if (column.filterable) {
1920
searchKeys.add(
@@ -23,10 +24,15 @@ const fuseIndex = memoizeOne(
2324
);
2425
}
2526
});
26-
return Fuse.createIndex([...searchKeys], data);
27+
return Array.from(searchKeys);
2728
}
2829
);
2930

31+
const fuseIndex = memoizeOne(
32+
(data: DataTableRowData[], keys: FuseOptionKey<DataTableRowData>[]) =>
33+
Fuse.createIndex(keys, data)
34+
);
35+
3036
const filterData = (
3137
data: DataTableRowData[],
3238
columns: SortableColumnContainer,
@@ -38,21 +44,13 @@ const filterData = (
3844
return data;
3945
}
4046

41-
const index = fuseIndex(data, columns);
47+
const keys = getSearchKeys(columns);
4248

43-
const fuse = new HaFuse(
44-
data,
45-
{ shouldSort: false, minMatchCharLength: 1 },
46-
index
47-
);
49+
const index = fuseIndex(data, keys);
4850

49-
const searchResults = fuse.multiTermsSearch(filter);
50-
51-
if (searchResults) {
52-
return searchResults.map((result) => result.item);
53-
}
54-
55-
return data;
51+
return multiTermSearch<DataTableRowData>(data, filter, keys, index, {
52+
threshold: 0.2, // reduce fuzzy matches in data tables
53+
});
5654
};
5755

5856
const sortData = (

src/components/device/ha-device-picker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { computeDeviceName } from "../../common/entity/compute_device_name";
99
import { getDeviceContext } from "../../common/entity/context/get_device_context";
1010
import { getConfigEntries, type ConfigEntry } from "../../data/config_entries";
1111
import {
12+
deviceComboBoxKeys,
1213
getDevices,
1314
type DevicePickerItem,
1415
type DeviceRegistryEntry,
@@ -216,6 +217,7 @@ export class HaDevicePicker extends LitElement {
216217
.getItems=${this._getItems}
217218
.hideClearIcon=${this.hideClearIcon}
218219
.valueRenderer=${valueRenderer}
220+
.searchKeys=${deviceComboBoxKeys}
219221
.unknownItemText=${this.hass.localize(
220222
"ui.components.device-picker.unknown"
221223
)}

src/components/entity/ha-entity-picker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { isValidEntityId } from "../../common/entity/valid_entity_id";
99
import { computeRTL } from "../../common/util/compute_rtl";
1010
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
1111
import {
12+
entityComboBoxKeys,
1213
getEntities,
1314
type EntityComboBoxItem,
1415
} from "../../data/entity_registry";
@@ -288,6 +289,7 @@ export class HaEntityPicker extends LitElement {
288289
.hideClearIcon=${this.hideClearIcon}
289290
.searchFn=${this._searchFn}
290291
.valueRenderer=${this._valueRenderer}
292+
.searchKeys=${entityComboBoxKeys}
291293
.addButtonLabel=${this.addButton
292294
? this.hass.localize("ui.components.entity.entity-picker.add")
293295
: undefined}

src/components/entity/ha-statistic-picker.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,21 @@ type StatisticItemType = "entity" | "external" | "no_state";
3838
interface StatisticComboBoxItem extends PickerComboBoxItem {
3939
statistic_id?: string;
4040
stateObj?: HassEntity;
41+
domainName?: string;
4142
type?: StatisticItemType;
4243
}
4344

45+
const SEARCH_KEYS = [
46+
{ name: "label", weight: 10 },
47+
{ name: "search_labels.entityName", weight: 10 },
48+
{ name: "search_labels.friendlyName", weight: 9 },
49+
{ name: "search_labels.deviceName", weight: 8 },
50+
{ name: "search_labels.areaName", weight: 6 },
51+
{ name: "search_labels.domainName", weight: 4 },
52+
{ name: "statisticId", weight: 3 },
53+
{ name: "id", weight: 2 },
54+
];
55+
4456
@customElement("ha-statistic-picker")
4557
export class HaStatisticPicker extends LitElement {
4658
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -233,7 +245,6 @@ export class HaStatisticPicker extends LitElement {
233245
),
234246
type,
235247
sorting_label: [sortingPrefix, label].join("_"),
236-
search_labels: [label, id],
237248
icon_path: mdiShape,
238249
});
239250
} else if (type === "external") {
@@ -246,7 +257,7 @@ export class HaStatisticPicker extends LitElement {
246257
secondary: domainName,
247258
type,
248259
sorting_label: [sortingPrefix, label].join("_"),
249-
search_labels: [label, domainName, id],
260+
search_labels: { label, domainName },
250261
icon_path: mdiChartLine,
251262
});
252263
}
@@ -280,13 +291,12 @@ export class HaStatisticPicker extends LitElement {
280291
stateObj: stateObj,
281292
type: "entity",
282293
sorting_label: [sortingPrefix, deviceName, entityName].join("_"),
283-
search_labels: [
284-
entityName,
285-
deviceName,
286-
areaName,
294+
search_labels: {
295+
entityName: entityName || null,
296+
deviceName: deviceName || null,
297+
areaName: areaName || null,
287298
friendlyName,
288-
id,
289-
].filter(Boolean) as string[],
299+
},
290300
});
291301
});
292302

@@ -361,13 +371,13 @@ export class HaStatisticPicker extends LitElement {
361371
stateObj: stateObj,
362372
type: "entity",
363373
sorting_label: [sortingPrefix, deviceName, entityName].join("_"),
364-
search_labels: [
365-
entityName,
366-
deviceName,
367-
areaName,
374+
search_labels: {
375+
entityName: entityName || null,
376+
deviceName: deviceName || null,
377+
areaName: areaName || null,
368378
friendlyName,
369379
statisticId,
370-
].filter(Boolean) as string[],
380+
},
371381
};
372382
}
373383

@@ -394,7 +404,7 @@ export class HaStatisticPicker extends LitElement {
394404
secondary: domainName,
395405
type: "external",
396406
sorting_label: [sortingPrefix, label].join("_"),
397-
search_labels: [label, domainName, statisticId],
407+
search_labels: { label, domainName, statisticId },
398408
icon_path: mdiChartLine,
399409
};
400410
}
@@ -409,7 +419,7 @@ export class HaStatisticPicker extends LitElement {
409419
secondary: this.hass.localize("ui.components.statistic-picker.no_state"),
410420
type: "no_state",
411421
sorting_label: [sortingPrefix, label].join("_"),
412-
search_labels: [label, statisticId],
422+
search_labels: { label, statisticId },
413423
icon_path: mdiShape,
414424
};
415425
}
@@ -475,6 +485,7 @@ export class HaStatisticPicker extends LitElement {
475485
.searchFn=${this._searchFn}
476486
.valueRenderer=${this._valueRenderer}
477487
.helper=${this.helper}
488+
.searchKeys=${SEARCH_KEYS}
478489
.unknownItemText=${this.hass.localize(
479490
"ui.components.statistic-picker.unknown"
480491
)}

0 commit comments

Comments
 (0)