diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index 288ee076..0715a728 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -5,7 +5,7 @@ import { SearchStore } from '../../services/SearchStore'; import { MenuItem } from '../SideMenu/MenuItem'; import { MarkerService } from '../../services/MarkerService'; -import { SearchDocument } from '../../services/SearchWorker.worker'; +import { SearchResult } from '../../services/SearchWorker.worker'; import { ClearIcon, @@ -16,7 +16,7 @@ import { } from './styled.elements'; export interface SearchBoxProps { - search: SearchStore; + search: SearchStore; marker: MarkerService; getItemById: (id: string) => IMenuItem | undefined; onActivate: (item: IMenuItem) => void; @@ -25,16 +25,11 @@ export interface SearchBoxProps { } export interface SearchBoxState { - results: any; + results: SearchResult[]; term: string; activeItemIdx: number; } -interface SearchResult { - item: IMenuItem; - score: number; -} - export class SearchBox extends React.PureComponent { activeItemRef: MenuItem | null = null; @@ -87,7 +82,7 @@ export class SearchBox extends React.PureComponent ({ - item: this.props.getItemById(res.id), + const results = this.state.results.map(res => ({ + item: this.props.getItemById(res.meta)!, score: res.score, })); diff --git a/src/services/AppStore.ts b/src/services/AppStore.ts index 5542942c..6c5b006e 100644 --- a/src/services/AppStore.ts +++ b/src/services/AppStore.ts @@ -49,7 +49,7 @@ export class AppStore { spec: SpecStore; rawOptions: RedocRawOptions; options: RedocNormalizedOptions; - search: SearchStore; + search: SearchStore; marker = new MarkerService(); private scroll: ScrollService; diff --git a/src/services/SearchStore.ts b/src/services/SearchStore.ts index 0b0d68bd..ed8a616d 100644 --- a/src/services/SearchStore.ts +++ b/src/services/SearchStore.ts @@ -17,7 +17,7 @@ if (IS_BROWSER) { worker = require('./SearchWorker.worker').default; } -export class SearchStore { +export class SearchStore { searchWorker = new worker(); indexItems(groups: Array) { @@ -34,12 +34,12 @@ export class SearchStore { this.searchWorker.done(); } - add(title: string, body: string, ref: string) { - this.searchWorker.add(title, body, ref); + add(title: string, body: string, meta?: T) { + this.searchWorker.add(title, body, meta); } search(q: string) { - return this.searchWorker.search(q); + return this.searchWorker.search(q); } async toJS() { diff --git a/src/services/SearchWorker.worker.ts b/src/services/SearchWorker.worker.ts index 449d1336..14927574 100644 --- a/src/services/SearchWorker.worker.ts +++ b/src/services/SearchWorker.worker.ts @@ -2,9 +2,9 @@ import * as lunr from 'lunr'; /* just for better typings */ export default class Worker { - add = add; + add: typeof add = add; done = done; - search = search; + search: typeof search = search; toJS = toJS; load = load; } @@ -15,11 +15,12 @@ export interface SearchDocument { id: string; } -export interface SearchResult extends SearchDocument { +export interface SearchResult { + meta: T; score: number; } -let store: { [id: string]: SearchDocument } = {}; +let store: any[] = []; let resolveIndex: (v: lunr.Index) => void = () => { throw new Error('Should not be called'); @@ -29,19 +30,21 @@ const index: Promise = new Promise(resolve => { resolveIndex = resolve; }); +lunr.tokenizer.separator = /\s+/; + const builder = new lunr.Builder(); builder.field('title'); builder.field('description'); -builder.ref('id'); +builder.ref('ref'); builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer); const expandTerm = term => '*' + lunr.stemmer(new lunr.Token(term, {})) + '*'; -export function add(title: string, description: string, id: string) { - const item = { title, description, id }; +export function add(title: string, description: string, meta?: T) { + const ref = store.push(meta) - 1; + const item = { title: title.toLowerCase(), description: description.toLowerCase(), ref }; builder.add(item); - store[id] = item; } export async function done() { @@ -60,20 +63,28 @@ export async function load(state: any) { resolveIndex(lunr.Index.load(state.index)); } -export async function search(q: string): Promise> { +export async function search( + q: string, + limit = 0, +): Promise>> { if (q.trim().length === 0) { return []; } - return (await index) - .query(t => { - q - .trim() - .split(/\s+/) - .forEach(term => { - const exp = expandTerm(term); - t.term(exp, {}); - }); - }) - .map(res => ({ ...store[res.ref], score: res.score })); + let searchResults = (await index).query(t => { + q + .trim() + .toLowerCase() + .split(/\s+/) + .forEach(term => { + const exp = expandTerm(term); + t.term(exp, {}); + }); + }); + + if (limit > 0) { + searchResults = searchResults.slice(0, limit); + } + + return searchResults.map(res => ({ meta: store[res.ref], score: res.score })); }