From eb790fb6ac030e7cfba9b62e3dea70a55827b459 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Tue, 13 Feb 2024 13:37:58 -0800 Subject: [PATCH] update search algorithm --- src/app/db/data.ts | 10 ++++---- src/app/search/page.tsx | 9 +++++++ src/app/utils/search.worker.ts | 44 +++++++++++++++++++++++++++++----- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/app/db/data.ts b/src/app/db/data.ts index 8ff92a3..a909d4a 100644 --- a/src/app/db/data.ts +++ b/src/app/db/data.ts @@ -66,7 +66,7 @@ export interface DocumentManifest { status: DocumentStatus reviewers?: reviewer[] } -export const documents: { [key: string]: Document } = { +export const documents: Readonly<{ [key: string]: Document }> = { 'day-5-principles': { manifest: { title: 'Day 5 Principles', @@ -316,7 +316,7 @@ export interface Topic { description: string wiki: string } -export const topics: { [key: string]: Topic } = { +export const topics: Readonly<{ [key: string]: Topic }> = { frc: { name: 'FIRST Robotics Competition', description: @@ -386,7 +386,7 @@ export interface Author { bio?: string website?: string } -export const authors: { [key: string]: Author } = { +export const authors: Readonly<{ [key: string]: Author }> = { shasan: { name: { first: 'Saim', @@ -533,7 +533,7 @@ export interface Affiliation { description: string } -export const affiliations: { [key: string]: Affiliation } = { +export const affiliations: Readonly<{ [key: string]: Affiliation }> = { '1280-mech': { name: "Team 1280, the Ragin' C Biscuits, Mechanical Subteam", short: '1280 Mech', @@ -689,7 +689,7 @@ export interface Nationality { demonym: string flag: string } -export const nationalities: { [key: string]: Nationality } = { +export const nationalities: Readonly<{ [key: string]: Nationality }> = { pak: { name: 'Pakistan', demonym: 'Pakistani', diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index 97e76f1..e1d0890 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -19,6 +19,15 @@ const SearchResult = ({ const { manifest, abstract, id } = result const { title, authors, topics, dates, status, type } = manifest + /* + this is not a recommended design pattern for creating a clickable object, + as it's terrible for accessibility. we should add accessibility tags to make it + recognizable as a clickable item. + the reason we aren't simply wrapping the search result in a tag is because + React does not support nested tags and it overrides the links displayed inside. + this click handling logic simply checks to see if the element clicked is a nested link + and fires a redirect if not, to avoid overriding links in the children + */ const handleClick = (event: React.MouseEvent) => { let target = event.target as HTMLElement while (target != null) { diff --git a/src/app/utils/search.worker.ts b/src/app/utils/search.worker.ts index b3977e8..b8f2820 100644 --- a/src/app/utils/search.worker.ts +++ b/src/app/utils/search.worker.ts @@ -1,29 +1,61 @@ -import { documents } from '@/app/db/data' +import { documents, authors, affiliations, topics } from '@/app/db/data' import MiniSearch from 'minisearch' import { CustomSearchResult } from './searchDocs' +// converting the documents object into an array that can be index by minisearch const docs = Object.entries(documents).map(([key, value]) => ({ id: key, keywords: value.manifest.keywords.join(' '), abstract: value.abstract, - topics: value.manifest.topics.join(' '), - authors: value.manifest.authors.join(' '), + topics: value.manifest.topics + .map((topicId) => topics[topicId]) + .map((topic) => topic.name) + .join(' '), + authors: value.manifest.authors // pull out author data from the user id + .map((authorId) => authors[authorId]) + .map( + (author) => + `${author.name.first} ${author.name.last} ${author.name.nickname}` + ) + .join(' '), title: value.manifest.title, manifest: value.manifest, type: value.manifest.type, + affiliations: value.manifest.authors // pull the affiliation metadata from the author data + .map((authorId) => authors[authorId]) + .map((author) => author.affiliation) + .map((affiliationId) => + affiliationId + .map( + (af) => + `${affiliations[af.split('@')[1]].name} ${affiliations[af.split('@')[1]].short}` + ) + .join(' ') + ) + .join(' '), + key: key, })) const miniSearch = new MiniSearch({ - fields: ['abstract', 'keywords', 'topics', 'authors', 'title', 'type'], + fields: [ + 'abstract', + 'keywords', + 'topics', + 'authors', + 'title', + 'type', + 'affiliations', + ], storeFields: ['key', 'abstract', 'manifest'], searchOptions: { boost: { - title: 2, + title: 3, keywords: 2, topics: 1, authors: 2, + abstract: 0.3, }, - fuzzy: 0.2, + fuzzy: 2, prefix: true, }, })