fix: reduce search index size

This commit is contained in:
Roman Hotsiy 2018-05-18 15:13:46 +03:00
parent 8afae474c7
commit a1fa4b47a8
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
4 changed files with 43 additions and 37 deletions

View File

@ -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<string>;
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<SearchBoxProps, SearchBoxState> {
activeItemRef: MenuItem | null = null;
@ -87,7 +82,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
// enter
const activeResult = this.state.results[this.state.activeItemIdx];
if (activeResult) {
const item = this.props.getItemById(activeResult.id);
const item = this.props.getItemById(activeResult.meta);
if (item) {
this.props.onActivate(item);
}
@ -95,7 +90,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
}
};
setResults(results: SearchDocument[], term: string) {
setResults(results: SearchResult[], term: string) {
this.setState({
results,
term,
@ -121,8 +116,8 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
render() {
const { activeItemIdx } = this.state;
const results: SearchResult[] = this.state.results.map(res => ({
item: this.props.getItemById(res.id),
const results = this.state.results.map(res => ({
item: this.props.getItemById(res.meta)!,
score: res.score,
}));

View File

@ -49,7 +49,7 @@ export class AppStore {
spec: SpecStore;
rawOptions: RedocRawOptions;
options: RedocNormalizedOptions;
search: SearchStore;
search: SearchStore<string>;
marker = new MarkerService();
private scroll: ScrollService;

View File

@ -17,7 +17,7 @@ if (IS_BROWSER) {
worker = require('./SearchWorker.worker').default;
}
export class SearchStore {
export class SearchStore<T> {
searchWorker = new worker();
indexItems(groups: Array<IMenuItem | OperationModel>) {
@ -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<T>(q);
}
async toJS() {

View File

@ -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<T = string> {
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<lunr.Index> = 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<T>(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<Array<SearchDocument & SearchResult>> {
export async function search<Meta = string>(
q: string,
limit = 0,
): Promise<Array<SearchResult<Meta>>> {
if (q.trim().length === 0) {
return [];
}
return (await index)
.query(t => {
let searchResults = (await index).query(t => {
q
.trim()
.toLowerCase()
.split(/\s+/)
.forEach(term => {
const exp = expandTerm(term);
t.term(exp, {});
});
})
.map(res => ({ ...store[res.ref], score: res.score }));
});
if (limit > 0) {
searchResults = searchResults.slice(0, limit);
}
return searchResults.map(res => ({ meta: store[res.ref], score: res.score }));
}