mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-11 03:16:48 +03:00
fix: reduce search index size
This commit is contained in:
parent
8afae474c7
commit
a1fa4b47a8
|
@ -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,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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 }));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user