diff --git a/web/packages/new/photos/components/SearchBar.tsx b/web/packages/new/photos/components/SearchBar.tsx index e6fa1c87d7..a6035f5faa 100644 --- a/web/packages/new/photos/components/SearchBar.tsx +++ b/web/packages/new/photos/components/SearchBar.tsx @@ -483,6 +483,7 @@ const labelForOption = (option: SearchOption) => { case "date": return t("date"); + case "location": return t("location"); diff --git a/web/packages/new/photos/services/search/index.ts b/web/packages/new/photos/services/search/index.ts index 5a13e77768..d82c1d731e 100644 --- a/web/packages/new/photos/services/search/index.ts +++ b/web/packages/new/photos/services/search/index.ts @@ -160,11 +160,11 @@ const localizedSearchData = () => locale: i18n.language, holidays: holidays().map((h) => ({ ...h, - lowercasedName: h.label.toLowerCase(), + searchTerms: h.label.toLowerCase().split(" "), })), labelledFileTypes: labelledFileTypes().map((t) => ({ ...t, - lowercasedName: t.label.toLowerCase(), + searchTerms: t.label.toLowerCase().split(" "), })), }); diff --git a/web/packages/new/photos/services/search/types.ts b/web/packages/new/photos/services/search/types.ts index 32639ee07a..ec59d70112 100644 --- a/web/packages/new/photos/services/search/types.ts +++ b/web/packages/new/photos/services/search/types.ts @@ -63,17 +63,17 @@ export interface LabelledFileType { } /** - * An annotated version of {@link T} that includes its searchable "lowercased" - * label or name. + * An annotated version of {@link T} that includes a list of lowercased words + * that it should match. * * Precomputing these lowercased values saves us from doing the lowercasing * during the search itself. */ export type Searchable = T & { /** - * The name or label of T, lowercased. + * The name or label of T, split into words and lowercased. */ - lowercasedName: string; + searchTerms: string[]; }; /** diff --git a/web/packages/new/photos/services/search/worker.ts b/web/packages/new/photos/services/search/worker.ts index b7f2c49ec7..31cb50458d 100644 --- a/web/packages/new/photos/services/search/worker.ts +++ b/web/packages/new/photos/services/search/worker.ts @@ -53,13 +53,13 @@ export class SearchWorker { .then((ts) => { this.locationTags = ts.map((t) => ({ ...t, - lowercasedName: t.name.toLowerCase(), + searchTerms: t.name.toLowerCase().split(" "), })); }), fetchCities().then((cs) => { this.cities = cs.map((c) => ({ ...c, - lowercasedName: c.name.toLowerCase(), + searchTerms: c.name.toLowerCase().split(" "), })); }), ]); @@ -77,7 +77,10 @@ export class SearchWorker { */ setPeople(people: Person[]) { this.searchablePeople = people.map((person) => { - return { ...person, lowercasedName: person.name.toLowerCase() }; + return { + ...person, + searchTerms: person.name.toLowerCase().split(" "), + }; }); } @@ -165,9 +168,17 @@ const fileTypeSuggestions = ( labelledFileTypes: Searchable[], ): SearchSuggestion[] => labelledFileTypes - .filter(({ lowercasedName }) => lowercasedName.startsWith(s)) + .filter(searchableMatch(s)) .map(({ fileType, label }) => ({ type: "fileType", fileType, label })); +/** + * A helper function to directly pass to filters on Searchable[]. + */ +const searchableMatch = + (s: string) => + ({ searchTerms }: { searchTerms: string[] }) => + searchTerms.some((t) => t.startsWith(s)); + const fileNameSuggestion = ( s: string, searchString: string, @@ -211,7 +222,7 @@ const peopleSuggestions = ( searchablePeople: Searchable[], ): SearchSuggestion[] => searchablePeople - .filter(({ lowercasedName }) => lowercasedName.startsWith(s)) + .filter(searchableMatch(s)) .map((person) => ({ type: "person", person, label: person.name })); const dateSuggestions = ( @@ -247,7 +258,7 @@ const parseDateComponents = ( [ parseChrono(s, locale), parseYearComponents(s), - holidays.filter(searchableIncludes(s)), + holidays.filter(searchableMatch(s)), ].flat(); const parseChrono = ( @@ -299,14 +310,6 @@ const parseYearComponents = (s: string): LabelledSearchDateComponents[] => { return []; }; -/** - * A helper function to directly pass to filters on Searchable[]. - */ -const searchableIncludes = - (s: string) => - ({ lowercasedName }: { lowercasedName: string }) => - lowercasedName.includes(s); - /** * Zod schema describing world_cities.json. * @@ -335,17 +338,15 @@ const locationSuggestions = ( locationTags: Searchable[], cities: Searchable[], ): SearchSuggestion[] => { - const matchingLocationTags = locationTags.filter(searchableIncludes(s)); + const matchingLocationTags = locationTags.filter(searchableMatch(s)); const matchingLocationTagLNames = new Set( - matchingLocationTags.map((t) => t.lowercasedName), + matchingLocationTags.map((t) => t.searchTerms), ); - const matchingCities = cities.filter( - (c) => - c.lowercasedName.startsWith(s) && - !matchingLocationTagLNames.has(c.lowercasedName), - ); + const matchingCities = cities + .filter(searchableMatch(s)) + .filter((c) => !matchingLocationTagLNames.has(c.searchTerms)); return [ matchingLocationTags.map(