2018-11-07 08:43:48 +01:00
|
|
|
import { Injectable } from '@angular/core';
|
2019-02-01 13:56:08 +01:00
|
|
|
import { Searchable } from '../../site/base/searchable';
|
|
|
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
2019-02-28 08:52:46 +01:00
|
|
|
import { BaseRepository } from '../repositories/base-repository';
|
|
|
|
import { ViewModelStoreService } from '../core-services/view-model-store.service';
|
2018-11-07 08:43:48 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2019-02-28 08:52:46 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether to open the detail page in a new tab.
|
|
|
|
*/
|
|
|
|
openInNewTab: boolean;
|
2018-11-07 08:43:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2019-02-28 08:52:46 +01:00
|
|
|
/**
|
|
|
|
* Whether to open the detail page in a new tab.
|
|
|
|
*/
|
|
|
|
openInNewTab: boolean;
|
|
|
|
|
2018-11-07 08:43:48 +01:00
|
|
|
/**
|
|
|
|
* All matched models.
|
|
|
|
*/
|
2019-02-01 13:56:08 +01:00
|
|
|
models: (BaseViewModel & Searchable)[];
|
2018-11-07 08:43:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2019-02-28 08:52:46 +01:00
|
|
|
openInNewTab: boolean;
|
2018-11-07 08:43:48 +01:00
|
|
|
}[] = [];
|
|
|
|
|
|
|
|
/**
|
2019-02-28 08:52:46 +01:00
|
|
|
* @param viewModelStore The store to search in.
|
2018-11-07 08:43:48 +01:00
|
|
|
*/
|
2019-02-28 08:52:46 +01:00
|
|
|
public constructor(private viewModelStore: ViewModelStoreService) {}
|
2018-11-07 08:43:48 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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,
|
2019-02-28 08:52:46 +01:00
|
|
|
repo: BaseRepository<any, any>,
|
|
|
|
displayOrder: number,
|
|
|
|
openInNewTab: boolean = false
|
2018-11-07 08:43:48 +01:00
|
|
|
): void {
|
2019-02-08 16:02:46 +01:00
|
|
|
// const instance = new ctor();
|
2018-11-07 08:43:48 +01:00
|
|
|
this.searchModels.push({
|
|
|
|
collectionString: collectionString,
|
2019-02-28 08:52:46 +01:00
|
|
|
verboseNameSingular: repo.getVerboseName(),
|
|
|
|
verboseNamePlural: repo.getVerboseName(true),
|
|
|
|
displayOrder: displayOrder,
|
|
|
|
openInNewTab: openInNewTab
|
2018-11-07 08:43:48 +01:00
|
|
|
});
|
|
|
|
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,
|
2019-02-28 08:52:46 +01:00
|
|
|
verboseNamePlural: searchModel.verboseNamePlural,
|
|
|
|
openInNewTab: searchModel.openInNewTab
|
2018-11-07 08:43:48 +01:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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();
|
2019-02-28 08:52:46 +01:00
|
|
|
return this.searchModels
|
2018-11-07 08:43:48 +01:00
|
|
|
.filter(s => inCollectionStrings.includes(s.collectionString))
|
|
|
|
.map(searchModel => {
|
2019-02-28 08:52:46 +01:00
|
|
|
const results = this.viewModelStore
|
|
|
|
.getAll(searchModel.collectionString)
|
|
|
|
.map(x => x as (BaseViewModel & Searchable))
|
|
|
|
.filter(model => model.formatForSearch().some(text => text.toLowerCase().includes(query)));
|
2018-11-07 08:43:48 +01:00
|
|
|
return {
|
|
|
|
collectionString: searchModel.collectionString,
|
|
|
|
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
|
2019-02-28 08:52:46 +01:00
|
|
|
openInNewTab: searchModel.openInNewTab,
|
2018-11-07 08:43:48 +01:00
|
|
|
models: results
|
|
|
|
};
|
2019-02-28 08:52:46 +01:00
|
|
|
});
|
2018-11-07 08:43:48 +01:00
|
|
|
}
|
|
|
|
}
|