Improve search accuracy in the app (instructions, examples, objects, tags) (#4906)

* Use tokenized search to improve search with multiple words looking into multiple keys (ex: name & description)
This commit is contained in:
Clément Pasteau
2023-02-08 09:41:27 +01:00
committed by GitHub
parent d61f8336a8
commit 32b97f2c40
4 changed files with 78 additions and 29 deletions

View File

@@ -27,6 +27,7 @@ import {
tuneMatches,
type SearchResult,
sharedFuseConfiguration,
getFuseSearchQueryForMultipleKeys,
} from '../../../UI/Search/UseSearchStructuredItem';
const gd: libGDevelop = global.gd;
@@ -107,17 +108,19 @@ export default class InstructionOrExpressionSelector<
} = this.props;
const { searchText } = this.state;
const extendSearchText = `'${searchText
.trim()
.split(' ')
.join(" '")}`;
const displayedInstructionsList: Array<SearchResult<T>> =
!!extendSearchText && this.searchApi
? this.searchApi.search(extendSearchText).map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))
!!searchText && this.searchApi
? this.searchApi
.search(
getFuseSearchQueryForMultipleKeys(searchText, [
'displayedName',
'fullGroupName',
])
)
.map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))
: [];
const hasResults = !searchText || !!displayedInstructionsList.length;

View File

@@ -55,6 +55,8 @@ import {
type SearchResult,
tuneMatches,
sharedFuseConfiguration,
getFuseSearchQueryForSimpleArray,
getFuseSearchQueryForMultipleKeys,
} from '../../UI/Search/UseSearchStructuredItem';
import { Column, Line } from '../../UI/Grid';
@@ -193,33 +195,37 @@ export default class InstructionOrObjectSelector extends React.PureComponent<
_search = (searchText: string) => {
if (searchText === '') return;
const extendSearchText = `'${searchText
.trim()
.split(' ')
.join(" '")}`;
const extendedSearchText = getFuseSearchQueryForSimpleArray(searchText);
this.setState({
searchResults: {
objects: this.objectSearchApi
? this.objectSearchApi.search(extendSearchText).map(result => ({
? this.objectSearchApi.search(extendedSearchText).map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))
: [],
groups: this.groupSearchApi
? this.groupSearchApi.search(extendSearchText).map(result => ({
? this.groupSearchApi.search(extendedSearchText).map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))
: [],
instructions: this.instructionSearchApi
? this.instructionSearchApi.search(extendSearchText).map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))
? this.instructionSearchApi
.search(
getFuseSearchQueryForMultipleKeys(searchText, [
'displayedName',
'fullGroupName',
])
)
.map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))
: [],
tags: this.tagSearchApi
? this.tagSearchApi.search(extendSearchText).map(result => ({
? this.tagSearchApi.search(extendedSearchText).map(result => ({
item: result.item,
matches: tuneMatches(result, searchText),
}))

View File

@@ -730,13 +730,13 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
{
label: i18n._(t`Tags`),
submenu: buildTagsMenuTemplate({
noTagLabel: 'No tags',
noTagLabel: i18n._(t`No tags`),
getAllTags: getAllObjectTags,
selectedTags: getTagsFromString(object.getTags()),
onChange: objectTags => {
changeObjectTags(object, objectTags);
},
editTagsLabel: 'Add/edit tags...',
editTagsLabel: i18n._(t`Add/edit tags...`),
onEditTags: () => openEditTagDialog(object),
}),
},

View File

@@ -30,6 +30,43 @@ export const sharedFuseConfiguration = {
includeMatches: true,
ignoreLocation: true,
useExtendedSearch: true,
findAllMatches: true,
};
/**
* This helper allows creating the search query for a search within a simple array of strings.
*/
export const getFuseSearchQueryForSimpleArray = (searchText: string) => {
const tokenisedSearchQuery = searchText.trim().split(' ');
return `'${tokenisedSearchQuery.join(" '")}`;
};
/**
* This helper allows creating the search query for searching within an array of
* objects with multiple keys.
* If we don't use this, the search will only be done on one of the keys.
* See https://github.com/krisk/Fuse/issues/235#issuecomment-850269634
*/
export const getFuseSearchQueryForMultipleKeys = (
searchText: string,
keys: Array<string>
) => {
const tokenisedSearchQuery = searchText.trim().split(' ');
const searchQuery: {
$or: Fuse.Expression[],
}[] = tokenisedSearchQuery.map((searchToken: string) => {
const orFields: Fuse.Expression[] = keys.map(key => ({
[key]: searchToken,
}));
return {
$or: orFields,
};
});
return {
$and: searchQuery,
};
};
const tuneMatchIndices = (match: SearchMatch, searchText: string) => {
@@ -213,6 +250,7 @@ export const useSearchStructuredItem = <
includeMatches: true,
ignoreLocation: true,
useExtendedSearch: true,
findAllMatches: true,
});
const totalTime = performance.now() - startTime;
@@ -253,13 +291,15 @@ export const useSearchStructuredItem = <
return;
}
const extendSearchText = `'${searchText
.trim()
.split(' ')
.join(" '")}`;
const startTime = performance.now();
const results = searchApi.search(extendSearchText);
const results = searchApi.search(
getFuseSearchQueryForMultipleKeys(searchText, [
'name',
'fullName',
'shortDescription',
'tags',
])
);
const totalTime = performance.now() - startTime;
console.info(
`Found ${results.length} items in ${totalTime.toFixed(3)}ms.`