OpenSlides/client/src/app/core/ui-services/search.service.ts
FinnStutzenstein 2e0353618d Revive the global search.
Fixed leftovers from the viewModel restructure. Repositories always have a verboseName now. Does not implement new logic to follow related models (e.g. motions->submitters).
2019-02-28 08:56:03 +01:00

147 lines
4.4 KiB
TypeScript

import { Injectable } from '@angular/core';
import { Searchable } from '../../site/base/searchable';
import { BaseViewModel } from 'app/site/base/base-view-model';
import { BaseRepository } from '../repositories/base-repository';
import { ViewModelStoreService } from '../core-services/view-model-store.service';
/**
* The representation every searchable model should use to represent their data.
*/
export type SearchRepresentation = string[];
/**
* Our representation of a searchable model for external use.
*/
export interface SearchModel {
/**
* The collection string.
*/
collectionString: string;
/**
* The singular verbose name of the model.
*/
verboseNameSingular: string;
/**
* The plural verbose name of the model.
*/
verboseNamePlural: string;
/**
* Whether to open the detail page in a new tab.
*/
openInNewTab: boolean;
}
/**
* A search result has the model's collectionstring, a verbose name and the actual models.
*/
export interface SearchResult {
/**
* The collection string.
*/
collectionString: string;
/**
* This verbodeName must have the right cardianlity. If there is exactly one model in `models`,
* it should have a singular value, else a plural name.
*/
verboseName: string;
/**
* Whether to open the detail page in a new tab.
*/
openInNewTab: boolean;
/**
* All matched models.
*/
models: (BaseViewModel & Searchable)[];
}
/**
* This service cares about searching the DataStore and managing models, that are searchable.
*/
@Injectable({
providedIn: 'root'
})
export class SearchService {
/**
* All searchable models in our own representation.
*/
private searchModels: {
collectionString: string;
verboseNameSingular: string;
verboseNamePlural: string;
displayOrder: number;
openInNewTab: boolean;
}[] = [];
/**
* @param viewModelStore The store to search in.
*/
public constructor(private viewModelStore: ViewModelStoreService) {}
/**
* Registers a model by the given attributes.
*
* @param collectionString The colelction string of the model
* @param ctor The model constructor
* @param displayOrder The order in which the elements should be displayed.
*/
public registerModel(
collectionString: string,
repo: BaseRepository<any, any>,
displayOrder: number,
openInNewTab: boolean = false
): void {
// const instance = new ctor();
this.searchModels.push({
collectionString: collectionString,
verboseNameSingular: repo.getVerboseName(),
verboseNamePlural: repo.getVerboseName(true),
displayOrder: displayOrder,
openInNewTab: openInNewTab
});
this.searchModels.sort((a, b) => a.displayOrder - b.displayOrder);
}
/**
* @returns all registered models for the UI.
*/
public getRegisteredModels(): SearchModel[] {
return this.searchModels.map(searchModel => ({
collectionString: searchModel.collectionString,
verboseNameSingular: searchModel.verboseNameSingular,
verboseNamePlural: searchModel.verboseNamePlural,
openInNewTab: searchModel.openInNewTab
}));
}
/**
* Does the actual searching.
*
* @param query The search query
* @param inCollectionStrings All connection strings which should be used for searching.
* @returns All search results.
*/
public search(query: string, inCollectionStrings: string[]): SearchResult[] {
query = query.toLowerCase();
return this.searchModels
.filter(s => inCollectionStrings.includes(s.collectionString))
.map(searchModel => {
const results = this.viewModelStore
.getAll(searchModel.collectionString)
.map(x => x as (BaseViewModel & Searchable))
.filter(model => model.formatForSearch().some(text => text.toLowerCase().includes(query)));
return {
collectionString: searchModel.collectionString,
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
openInNewTab: searchModel.openInNewTab,
models: results
};
});
}
}