Disable various expensive sorting functions

Prevents the usage of: "sortViewUsersByConfig" of the userRepository
since it's an incredibe heavy operation for ~500 (real) users.

I would advice to sort the datastore rather than the lists
in the view to prevent unnecessary sorting overhead
This commit is contained in:
Sean Engelhardt 2019-02-22 17:17:29 +01:00
parent 497a3ec71d
commit 3785545a35
8 changed files with 67 additions and 38 deletions

View File

@ -115,6 +115,8 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
* *
* @param categories * @param categories
* @returns the categories sorted by prefix or name, according to the config setting * @returns the categories sorted by prefix or name, according to the config setting
*
* TODO: That operation is HEAVY
*/ */
public sortViewCategoriesByConfig(categories: ViewCategory[]): ViewCategory[] { public sortViewCategoriesByConfig(categories: ViewCategory[]): ViewCategory[] {
const sort = this.configService.instant<'prefix' | 'name'>('motions_category_sorting') || 'prefix'; const sort = this.configService.instant<'prefix' | 'name'>('motions_category_sorting') || 'prefix';

View File

@ -305,6 +305,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
/** /**
* Returns all duplicates of an user (currently: full name matches) * Returns all duplicates of an user (currently: full name matches)
*
* @param user * @param user
*/ */
public getUserDuplicates(user: ViewUser): ViewUser[] { public getUserDuplicates(user: ViewUser): ViewUser[] {
@ -313,6 +314,8 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
/** /**
* @returns the observable for users sorted according to configuration * @returns the observable for users sorted according to configuration
*
* TODO: This is leading to heavy operations
*/ */
public getSortedViewModelListObservable(): Observable<ViewUser[]> { public getSortedViewModelListObservable(): Observable<ViewUser[]> {
const subject = new BehaviorSubject<ViewUser[]>([]); const subject = new BehaviorSubject<ViewUser[]>([]);
@ -329,6 +332,8 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
* @returns the users sorted by first name, last name or number, according * @returns the users sorted by first name, last name or number, according
* to the config setting. Fallthrough and identical cases will be sorted by * to the config setting. Fallthrough and identical cases will be sorted by
* 'short_name' * 'short_name'
*
* TODO: That operation is HEAVY
*/ */
public sortViewUsersByConfig(users: ViewUser[]): ViewUser[] { public sortViewUsersByConfig(users: ViewUser[]): ViewUser[] {
const sort = this.configService.instant<'first_name' | 'last_name' | 'number'>('users_sort_by') || 'last_name'; const sort = this.configService.instant<'first_name' | 'last_name' | 'number'>('users_sort_by') || 'last_name';

View File

@ -90,9 +90,12 @@
<ng-container matColumnDef="menu"> <ng-container matColumnDef="menu">
<mat-header-cell *matHeaderCellDef mat-sort-header>Menu</mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header>Menu</mat-header-cell>
<mat-cell *matCellDef="let item"> <mat-cell *matCellDef="let item">
<button mat-icon-button <button
*osPerms="'agenda.can_manage'" mat-icon-button
[matMenuTriggerFor]="singleItemMenu" [matMenuTriggerData]="{ item: item }"> *osPerms="'agenda.can_manage'"
[matMenuTriggerFor]="singleItemMenu"
[matMenuTriggerData]="{ item: item }"
>
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
</mat-cell> </mat-cell>

View File

@ -65,9 +65,7 @@ export class ViewAssignment extends BaseAgendaViewModel {
this._tags = tags; this._tags = tags;
} }
public updateDependencies(update: BaseViewModel): void { public updateDependencies(update: BaseViewModel): void {}
console.log('TODO: assignment updateDependencies');
}
public getAgendaItem(): ViewItem { public getAgendaItem(): ViewItem {
return this.agendaItem; return this.agendaItem;

View File

@ -5,6 +5,7 @@ import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BaseComponent } from '../../base.component'; import { BaseComponent } from '../../base.component';
import { Subscription } from 'rxjs';
/** /**
* A base class for all views. Implements a generic error handling by raising a snack bar * A base class for all views. Implements a generic error handling by raising a snack bar
@ -17,6 +18,11 @@ export abstract class BaseViewComponent extends BaseComponent implements OnDestr
*/ */
private messageSnackBar: MatSnackBarRef<SimpleSnackBar>; private messageSnackBar: MatSnackBarRef<SimpleSnackBar>;
/**
* Subscriptions added to this list will be cleared 'on destroy'
*/
protected subscriptions: Subscription[];
/** /**
* Constructor for bas elist views * Constructor for bas elist views
* @param titleService the title serivce, passed to the base component * @param titleService the title serivce, passed to the base component
@ -25,6 +31,7 @@ export abstract class BaseViewComponent extends BaseComponent implements OnDestr
*/ */
public constructor(titleService: Title, translate: TranslateService, private matSnackBar: MatSnackBar) { public constructor(titleService: Title, translate: TranslateService, private matSnackBar: MatSnackBar) {
super(titleService, translate); super(titleService, translate);
this.subscriptions = [];
} }
/** /**
@ -58,11 +65,18 @@ export abstract class BaseViewComponent extends BaseComponent implements OnDestr
} }
/** /**
* automatically dismisses the error snack bar, if the component is destroyed. * automatically dismisses the error snack bar and clears subscriptions
* if the component is destroyed.
*/ */
public ngOnDestroy(): void { public ngOnDestroy(): void {
if (this.messageSnackBar) { if (this.messageSnackBar) {
this.messageSnackBar.dismiss(); this.messageSnackBar.dismiss();
} }
if (this.subscriptions.length > 0) {
for (const sub of this.subscriptions) {
sub.unsubscribe();
}
}
} }
} }

View File

@ -96,20 +96,24 @@ export abstract class ListViewBaseComponent<V extends BaseViewModel, M extends B
* Standard filtering function. Sufficient for most list views but can be overwritten * Standard filtering function. Sufficient for most list views but can be overwritten
*/ */
protected onFilter(): void { protected onFilter(): void {
this.filterService.filter().subscribe(filteredData => (this.sortService.data = filteredData)); this.subscriptions.push(
this.filterService.filter().subscribe(filteredData => (this.sortService.data = filteredData))
);
} }
/** /**
* Standard sorting function. Siffucient for most list views but can be overwritten * Standard sorting function. Siffucient for most list views but can be overwritten
*/ */
protected onSort(): void { protected onSort(): void {
this.sortService.sort().subscribe(sortedData => { this.subscriptions.push(
// the dataArray needs to be cleared (since angular 7) this.sortService.sort().subscribe(sortedData => {
// changes are not detected properly anymore // the dataArray needs to be cleared (since angular 7)
this.dataSource.data = []; // changes are not detected properly anymore
this.dataSource.data = sortedData; this.dataSource.data = [];
this.checkSelection(); this.dataSource.data = sortedData;
}); this.checkSelection();
})
);
} }
public onSortButton(itemProperty: string): void { public onSortButton(itemProperty: string): void {

View File

@ -80,12 +80,6 @@ export class ManageSubmittersComponent extends BaseViewComponent {
this.addSubmitterForm = new FormGroup({ userId: new FormControl([]) }); this.addSubmitterForm = new FormGroup({ userId: new FormControl([]) });
this.editSubmitterObservable = this.editSubmitterSubject.asObservable(); this.editSubmitterObservable = this.editSubmitterSubject.asObservable();
// get all users for the submitter add form
this.users = new BehaviorSubject<ViewUser[]>(
this.userRepository.sortViewUsersByConfig(this.userRepository.getViewModelList())
);
this.userRepository.getSortedViewModelListObservable().subscribe(users => this.users.next(users));
// detect changes in the form // detect changes in the form
this.addSubmitterForm.valueChanges.subscribe(formResult => { this.addSubmitterForm.valueChanges.subscribe(formResult => {
if (formResult && formResult.userId) { if (formResult && formResult.userId) {
@ -101,6 +95,10 @@ export class ManageSubmittersComponent extends BaseViewComponent {
this.isEditMode = true; this.isEditMode = true;
this.editSubmitterSubject.next(this.motion.submitters.map(x => x)); this.editSubmitterSubject.next(this.motion.submitters.map(x => x));
this.addSubmitterForm.reset(); this.addSubmitterForm.reset();
// get all users for the submitter add form
this.users = new BehaviorSubject<ViewUser[]>(this.userRepository.getViewModelList());
this.userRepository.getViewModelListObservable().subscribe(users => this.users.next(users));
} }
/** /**

View File

@ -402,16 +402,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
) { ) {
super(title, translate, matSnackBar); super(title, translate, matSnackBar);
// Initial Filling of the Subjects
this.submitterObserver = new BehaviorSubject(
this.userRepo.sortViewUsersByConfig(this.userRepo.getViewModelList())
);
this.supporterObserver = new BehaviorSubject(
this.userRepo.sortViewUsersByConfig(this.userRepo.getViewModelList())
);
this.categoryObserver = new BehaviorSubject(
this.categoryRepo.sortViewCategoriesByConfig(this.viewModelStore.getAll(ViewCategory))
);
this.workflowObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewWorkflow)); this.workflowObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewWorkflow));
this.blockObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewMotionBlock)); this.blockObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewMotionBlock));
this.mediafilesObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewMediafile)); this.mediafilesObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewMediafile));
@ -419,13 +409,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
this.tagObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewTag)); this.tagObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewTag));
this.motionObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewMotion)); this.motionObserver = new BehaviorSubject(this.viewModelStore.getAll(ViewMotion));
this.userRepo.getSortedViewModelListObservable().subscribe(sortedUsers => {
this.submitterObserver.next(sortedUsers);
this.supporterObserver.next(sortedUsers);
});
this.categoryRepo.getSortedViewModelListObservable().subscribe(sortedCategories => {
this.categoryObserver.next(sortedCategories);
});
// Make sure the subjects are updated, when a new Model for the type arrives // Make sure the subjects are updated, when a new Model for the type arrives
// TODO get rid of DS here // TODO get rid of DS here
this.DS.changeObservable.subscribe(newModel => { this.DS.changeObservable.subscribe(newModel => {
@ -477,6 +460,23 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
this.getMotionByUrl(); this.getMotionByUrl();
this.setSurroundingMotions(); this.setSurroundingMotions();
// TODO: Changed to un-sort, since it's a really heavy operation
this.userRepo.getViewModelListObservable().subscribe(unsortedUsers => {
this.submitterObserver.next(unsortedUsers);
this.supporterObserver.next(unsortedUsers);
});
this.categoryRepo.getViewModelListObservable().subscribe(unsortedCategories => {
this.categoryObserver.next(unsortedCategories);
});
// Initial Filling of the Subjects
this.submitterObserver = new BehaviorSubject(this.userRepo.getViewModelList());
this.supporterObserver = new BehaviorSubject(this.userRepo.getViewModelList());
this.categoryObserver = new BehaviorSubject(
this.categoryRepo.sortViewCategoriesByConfig(this.viewModelStore.getAll(ViewCategory))
);
this.statuteRepo.getViewModelListObservable().subscribe(newViewStatuteParagraphs => { this.statuteRepo.getViewModelListObservable().subscribe(newViewStatuteParagraphs => {
this.statuteParagraphs = newViewStatuteParagraphs; this.statuteParagraphs = newViewStatuteParagraphs;
}); });
@ -501,6 +501,10 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
}); });
} }
/**
* Called during view destruction.
* Sends a notification to user editors of the motion was edited
*/
public ngOnDestroy(): void { public ngOnDestroy(): void {
this.unsubscribeEditNotifications(TypeOfNotificationViewMotion.TYPE_CLOSING_EDITING_MOTION); this.unsubscribeEditNotifications(TypeOfNotificationViewMotion.TYPE_CLOSING_EDITING_MOTION);
} }
@ -1086,6 +1090,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
* then appending motion without identifiers sorted by title * then appending motion without identifiers sorted by title
*/ */
public setSurroundingMotions(): void { public setSurroundingMotions(): void {
// TODO: that operation is HEAVY
this.motionObserver.value.sort((a, b) => { this.motionObserver.value.sort((a, b) => {
if (a.identifier && b.identifier) { if (a.identifier && b.identifier) {
return a.identifier.localeCompare(b.identifier, this.translate.currentLang); return a.identifier.localeCompare(b.identifier, this.translate.currentLang);