/
search.service.ts
77 lines (67 loc) 路 2.82 KB
/
search.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { Injectable } from '@angular/core';
import { ActionsSubject, select, Store } from '@ngrx/store';
import * as fuzzysort from 'fuzzysort';
import { merge, of, zip } from 'rxjs';
import { filter, switchMap, take } from 'rxjs/operators';
import { ProjectsActionTypes } from '../../projects/state/projects.actions';
import { ProjectsSelectors } from '../../projects/state/projects.selectors';
import { AppSelectors } from '../../state/app.selectors';
import { ApplicationState } from '../../state/app.state';
import { CategoryEntities, CategoryEntity, ChecklistItem, ItemEntities } from '../models/checklist.model';
import { ChecklistSelectors } from '../state/checklist.selectors';
import { IndexEntry } from './search.models';
@Injectable()
export class SearchService {
private index: Array<IndexEntry<ChecklistItem | CategoryEntity>>;
private options: Fuzzysort.KeyOptions = {
key: 'value.title',
allowTypo: false,
limit: 100,
threshold: -10000
};
constructor(private store: Store<ApplicationState>, private actions: ActionsSubject) {
const actions$ = this.actions.pipe(filter(action => action.type === ProjectsActionTypes.TOGGLE_CATEGORY));
merge(actions$, of('INIT INDEX'))
.pipe(switchMap(_ => this.getStoreData()))
.subscribe(([categories, items, projectId]) => {
this.index = this.createIndex(categories, items, projectId);
});
}
search(term: string) {
return of(fuzzysort.go(term, this.index, this.options));
}
createIndex(categoryEntities: CategoryEntities, itemEntities: ItemEntities, projectId: string) {
const categories = Object.values(categoryEntities).map(category => this.compileCategory(category, projectId));
const items = categories.reduce(this.compileCategoryItems(itemEntities, projectId), []);
return [...categories, ...items];
}
private getStoreData() {
return zip(
this.store.pipe(select(ChecklistSelectors.getActiveCategoryEntities)),
this.store.pipe(select(AppSelectors.getItemEntities)),
this.store.pipe(select(ProjectsSelectors.getSelectedProjectId))
).pipe(take(1));
}
private compileCategory(category: CategoryEntity, projectId: string) {
return {
value: category,
link: `${this.getBaseLink(projectId)}/${category.slug}`
};
}
private compileCategoryItems(itemEntities: ItemEntities, projectId: string) {
return (acc: Array<IndexEntry<ChecklistItem>>, category: IndexEntry<CategoryEntity>) => {
return acc.concat(
category.value.items.map(itemId => {
const checklistItem = itemEntities[itemId];
return {
value: checklistItem,
link: `${this.getBaseLink(projectId)}/${checklistItem.category}/${checklistItem.id}`
};
})
);
};
}
private getBaseLink(projectId: string) {
return `/${projectId}/checklist`;
}
}