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 { MenuItem } from '../SideMenu/MenuItem';
import { MarkerService } from '../../services/MarkerService'; import { MarkerService } from '../../services/MarkerService';
import { SearchDocument } from '../../services/SearchWorker.worker'; import { SearchResult } from '../../services/SearchWorker.worker';
import { import {
ClearIcon, ClearIcon,
@ -16,7 +16,7 @@ import {
} from './styled.elements'; } from './styled.elements';
export interface SearchBoxProps { export interface SearchBoxProps {
search: SearchStore; search: SearchStore<string>;
marker: MarkerService; marker: MarkerService;
getItemById: (id: string) => IMenuItem | undefined; getItemById: (id: string) => IMenuItem | undefined;
onActivate: (item: IMenuItem) => void; onActivate: (item: IMenuItem) => void;
@ -25,16 +25,11 @@ export interface SearchBoxProps {
} }
export interface SearchBoxState { export interface SearchBoxState {
results: any; results: SearchResult[];
term: string; term: string;
activeItemIdx: number; activeItemIdx: number;
} }
interface SearchResult {
item: IMenuItem;
score: number;
}
export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> { export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> {
activeItemRef: MenuItem | null = null; activeItemRef: MenuItem | null = null;
@ -87,7 +82,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
// enter // enter
const activeResult = this.state.results[this.state.activeItemIdx]; const activeResult = this.state.results[this.state.activeItemIdx];
if (activeResult) { if (activeResult) {
const item = this.props.getItemById(activeResult.id); const item = this.props.getItemById(activeResult.meta);
if (item) { if (item) {
this.props.onActivate(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({ this.setState({
results, results,
term, term,
@ -121,8 +116,8 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
render() { render() {
const { activeItemIdx } = this.state; const { activeItemIdx } = this.state;
const results: SearchResult[] = this.state.results.map(res => ({ const results = this.state.results.map(res => ({
item: this.props.getItemById(res.id), item: this.props.getItemById(res.meta)!,
score: res.score, score: res.score,
})); }));

View File

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

View File

@ -17,7 +17,7 @@ if (IS_BROWSER) {
worker = require('./SearchWorker.worker').default; worker = require('./SearchWorker.worker').default;
} }
export class SearchStore { export class SearchStore<T> {
searchWorker = new worker(); searchWorker = new worker();
indexItems(groups: Array<IMenuItem | OperationModel>) { indexItems(groups: Array<IMenuItem | OperationModel>) {
@ -34,12 +34,12 @@ export class SearchStore {
this.searchWorker.done(); this.searchWorker.done();
} }
add(title: string, body: string, ref: string) { add(title: string, body: string, meta?: T) {
this.searchWorker.add(title, body, ref); this.searchWorker.add(title, body, meta);
} }
search(q: string) { search(q: string) {
return this.searchWorker.search(q); return this.searchWorker.search<T>(q);
} }
async toJS() { async toJS() {

View File

@ -2,9 +2,9 @@ import * as lunr from 'lunr';
/* just for better typings */ /* just for better typings */
export default class Worker { export default class Worker {
add = add; add: typeof add = add;
done = done; done = done;
search = search; search: typeof search = search;
toJS = toJS; toJS = toJS;
load = load; load = load;
} }
@ -15,11 +15,12 @@ export interface SearchDocument {
id: string; id: string;
} }
export interface SearchResult extends SearchDocument { export interface SearchResult<T = string> {
meta: T;
score: number; score: number;
} }
let store: { [id: string]: SearchDocument } = {}; let store: any[] = [];
let resolveIndex: (v: lunr.Index) => void = () => { let resolveIndex: (v: lunr.Index) => void = () => {
throw new Error('Should not be called'); throw new Error('Should not be called');
@ -29,19 +30,21 @@ const index: Promise<lunr.Index> = new Promise(resolve => {
resolveIndex = resolve; resolveIndex = resolve;
}); });
lunr.tokenizer.separator = /\s+/;
const builder = new lunr.Builder(); const builder = new lunr.Builder();
builder.field('title'); builder.field('title');
builder.field('description'); builder.field('description');
builder.ref('id'); builder.ref('ref');
builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer); builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer);
const expandTerm = term => '*' + lunr.stemmer(new lunr.Token(term, {})) + '*'; const expandTerm = term => '*' + lunr.stemmer(new lunr.Token(term, {})) + '*';
export function add(title: string, description: string, id: string) { export function add<T>(title: string, description: string, meta?: T) {
const item = { title, description, id }; const ref = store.push(meta) - 1;
const item = { title: title.toLowerCase(), description: description.toLowerCase(), ref };
builder.add(item); builder.add(item);
store[id] = item;
} }
export async function done() { export async function done() {
@ -60,20 +63,28 @@ export async function load(state: any) {
resolveIndex(lunr.Index.load(state.index)); 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) { if (q.trim().length === 0) {
return []; return [];
} }
return (await index) let searchResults = (await index).query(t => {
.query(t => {
q q
.trim() .trim()
.toLowerCase()
.split(/\s+/) .split(/\s+/)
.forEach(term => { .forEach(term => {
const exp = expandTerm(term); const exp = expandTerm(term);
t.term(exp, {}); 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 }));
} }