From 3d0999bd3efa9f58693929dddbbd49d6c252d9a6 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Mon, 8 Nov 2021 19:22:32 +0200 Subject: [PATCH] fix RED-2675: search screen not ordering by score --- .../dossier-details-stats.component.ts | 2 +- .../search-screen.component.html | 4 +- .../search-screen/search-screen.component.ts | 100 ++++++++---------- .../services/platform-search.service.ts | 12 ++- libs/red-domain/src/lib/search/index.ts | 2 + .../red-domain/src/lib/search/search-input.ts | 6 ++ .../src/lib/search/search-list-item.ts | 12 +++ 7 files changed, 76 insertions(+), 62 deletions(-) create mode 100644 libs/red-domain/src/lib/search/search-input.ts create mode 100644 libs/red-domain/src/lib/search/search-list-item.ts diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts index d52811feb..c09b132df 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts @@ -4,7 +4,7 @@ import { Dossier, DossierAttributeWithValue, DossierTemplate } from '@red/domain import { DossiersDialogService } from '../../../../services/dossiers-dialog.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; -import { FilesService } from '../../../../../../services/entity-services/files.service'; +import { FilesService } from '@services/entity-services/files.service'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html index a6be19bf0..768955d41 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html @@ -47,9 +47,7 @@ {{ 'search-screen.missing' | translate }}: {{ term }}. {{ 'search-screen.must-contain' | translate }}: -  {{ term }} diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts index 7f63f7a04..c409a94dc 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts @@ -1,7 +1,6 @@ -import { Component, forwardRef, Injector, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnDestroy } from '@angular/core'; import { DefaultListingServices, - IListable, keyChecker, List, ListingComponent, @@ -10,8 +9,8 @@ import { SearchPositions, TableColumnConfig, } from '@iqser/common-ui'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { debounceTime, map, skip, switchMap, tap } from 'rxjs/operators'; +import { merge, Observable } from 'rxjs'; +import { debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { fileStatusTranslations } from '../../translations/file-status-translations'; @@ -19,44 +18,38 @@ import { TranslateService } from '@ngx-translate/core'; import { RouterHistoryService } from '@services/router-history.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { PlatformSearchService } from '../../shared/services/platform-search.service'; -import { IMatchedDocument, ISearchResponse } from '@red/domain'; +import { IMatchedDocument, ISearchInput, ISearchListItem, ISearchResponse } from '@red/domain'; -interface ListItem extends IListable { - readonly dossierId: string; - readonly filename: string; - readonly unmatched: List | null; - readonly highlights: Record; - readonly routerLink: string; - readonly status: string; - readonly dossierName: string; - readonly numberOfPages: number; -} - -interface SearchInput { - readonly query: string; - readonly dossierIds?: List; +function toSearchInput(query: string, dossierIds: List | string): ISearchInput { + return { + query, + dossierIds: dossierIds ? (typeof dossierIds === 'string' ? [dossierIds] : dossierIds) : [], + }; } @Component({ templateUrl: './search-screen.component.html', styleUrls: ['./search-screen.component.scss'], providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => SearchScreenComponent) }], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SearchScreenComponent extends ListingComponent implements OnDestroy { +export class SearchScreenComponent extends ListingComponent implements OnDestroy { readonly fileStatusTranslations = fileStatusTranslations; readonly searchPositions = SearchPositions; readonly tableHeaderLabel = _('search-screen.table-header'); - readonly tableColumnConfigs: TableColumnConfig[] = [ + readonly tableColumnConfigs: TableColumnConfig[] = [ { label: _('search-screen.cols.document'), width: '2fr' }, { label: _('search-screen.cols.status') }, { label: _('search-screen.cols.dossier') }, { label: _('search-screen.cols.pages'), width: 'auto' }, ]; - readonly search$ = new BehaviorSubject(null); - readonly searchResults$: Observable = this.search$.asObservable().pipe( + + readonly searchResults$ = merge(this._searchChanged$, this._filtersChanged$).pipe( + startWith(this._routeQuery), tap(() => this._loadingService.start()), - switchMap(query => this._search(query)), + tap(value => this.updateNavigation(value.query)), + switchMap(query => this._platformSearchService.search(query)), map(searchResult => this._toMatchedDocuments(searchResult)), map(docs => this._toListItems(docs)), tap(result => this.entitiesService.setEntities(result)), @@ -76,6 +69,7 @@ export class SearchScreenComponent extends ListingComponent implements super(_injector); this.searchService.skip = true; + const dossierId = _activatedRoute.snapshot.queryParamMap.get('dossierId'); this.filterService.addFilterGroups([ { slug: 'dossiers', @@ -87,57 +81,53 @@ export class SearchScreenComponent extends ListingComponent implements new NestedFilter({ id: dossier.id, label: dossier.dossierName, + checked: dossier.id === dossierId, }), ), checker: keyChecker('dossierId'), }, ]); - - this.addSubscription = _activatedRoute.queryParamMap - .pipe(map(value => ({ query: value.get('query'), dossierId: value.get('dossierId') }))) - .subscribe(mappedValue => this._updateValues(mappedValue)); - - this.addSubscription = this.searchService.valueChanges$.pipe(debounceTime(300)).subscribe(value => this.updateNavigation(value)); - - this.addSubscription = this.filterService.filterGroups$.pipe(skip(1)).subscribe(group => { - const dossierIds = group[0].filters.filter(v => v.checked).map(v => v.id); - this.search$.next({ query: this.searchService.searchValue, dossierIds: dossierIds }); - }); } - updateNavigation(query: string, mustContain?: string): void { - const newQuery = query?.replace(mustContain, `"${mustContain}"`); - const queryParams = newQuery && newQuery !== '' ? { query: newQuery } : {}; - this._router.navigate([], { queryParams }).then(); + private get _searchChanged$(): Observable { + return this.searchService.valueChanges$.pipe( + debounceTime(300), + map(value => ({ query: value, dossierIds: [] })), + ); } - private _search(searchInput: SearchInput): Observable { - return this._platformSearchService.search({ - dossierIds: [...searchInput.dossierIds], - queryString: searchInput.query ?? '', - page: 1, - returnSections: false, - pageSize: 300, - }); + private get _filtersChanged$() { + return this.filterService.filterGroups$.pipe( + map(groups => groups[0].filters.filter(v => v.checked).map(v => v.id)), + map(dossierIds => toSearchInput(this.searchService.searchValue, dossierIds)), + ); } - private _updateValues({ query, dossierId }: { readonly query: string; readonly dossierId: string }) { - if (dossierId) { - this.filterService.toggleFilter('dossiers', dossierId); - } + private get _routeQuery(): ISearchInput { + const query = this._activatedRoute.snapshot.queryParamMap.get('query'); + const dossierId = this._activatedRoute.snapshot.queryParamMap.get('dossierId'); this.searchService.searchValue = query; - this.search$.next({ query, dossierIds: dossierId ? [dossierId] : [] }); + return { query, dossierIds: dossierId ? [dossierId] : [] }; + } + + updateNavigation(query: string) { + return this._router.navigate([], { queryParams: { query } }); + } + + mustContain(value: string) { + const newQuery = this.searchService.searchValue.replace(value, `"${value}"`); + this.searchService.searchValue = newQuery ?? ''; } private _toMatchedDocuments({ matchedDocuments }: ISearchResponse): IMatchedDocument[] { return matchedDocuments.filter(doc => doc.score > 0 && doc.matchedTerms.length > 0); } - private _toListItems(matchedDocuments: IMatchedDocument[]): ListItem[] { + private _toListItems(matchedDocuments: IMatchedDocument[]): ISearchListItem[] { return matchedDocuments.map(document => this._toListItem(document)).filter(value => value); } - private _toListItem({ dossierId, fileId, unmatchedTerms, highlights }: IMatchedDocument): ListItem { + private _toListItem({ dossierId, fileId, unmatchedTerms, highlights, score }: IMatchedDocument): ISearchListItem { const file = this._dossiersService.find(dossierId, fileId); if (!file) { return undefined; @@ -152,7 +142,7 @@ export class SearchScreenComponent extends ListingComponent implements numberOfPages: file.numberOfPages, dossierName: this._dossiersService.find(dossierId).dossierName, filename: file.filename, - searchKey: file.filename, + searchKey: score.toString(), routerLink: `/main/dossiers/${dossierId}/file/${fileId}`, }; } diff --git a/apps/red-ui/src/app/modules/dossier/shared/services/platform-search.service.ts b/apps/red-ui/src/app/modules/dossier/shared/services/platform-search.service.ts index 1eb744b27..19c11d681 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/services/platform-search.service.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/services/platform-search.service.ts @@ -1,6 +1,6 @@ import { Injectable, Injector } from '@angular/core'; import { GenericService } from '@iqser/common-ui'; -import { ISearchRequest, ISearchResponse } from '@red/domain'; +import { ISearchInput, ISearchRequest, ISearchResponse } from '@red/domain'; @Injectable() export class PlatformSearchService extends GenericService { @@ -8,7 +8,13 @@ export class PlatformSearchService extends GenericService { super(_injector, 'search'); } - search(body: ISearchRequest) { - return this._post(body); + search({ dossierIds, query }: ISearchInput) { + return this._post({ + dossierIds, + queryString: query ?? '', + page: 1, + returnSections: false, + pageSize: 300, + } as ISearchRequest); } } diff --git a/libs/red-domain/src/lib/search/index.ts b/libs/red-domain/src/lib/search/index.ts index 78ecaf160..536d4fccf 100644 --- a/libs/red-domain/src/lib/search/index.ts +++ b/libs/red-domain/src/lib/search/index.ts @@ -2,3 +2,5 @@ export * from './matched-document'; export * from './matched-section'; export * from './search.request'; export * from './search.response'; +export * from './search-list-item'; +export * from './search-input'; diff --git a/libs/red-domain/src/lib/search/search-input.ts b/libs/red-domain/src/lib/search/search-input.ts new file mode 100644 index 000000000..597b6391f --- /dev/null +++ b/libs/red-domain/src/lib/search/search-input.ts @@ -0,0 +1,6 @@ +import { List } from '@iqser/common-ui'; + +export interface ISearchInput { + readonly query: string; + readonly dossierIds?: List; +} diff --git a/libs/red-domain/src/lib/search/search-list-item.ts b/libs/red-domain/src/lib/search/search-list-item.ts new file mode 100644 index 000000000..c1b2b700b --- /dev/null +++ b/libs/red-domain/src/lib/search/search-list-item.ts @@ -0,0 +1,12 @@ +import { IListable, List } from '@iqser/common-ui'; + +export interface ISearchListItem extends IListable { + readonly dossierId: string; + readonly filename: string; + readonly unmatched: List | null; + readonly highlights: Record; + readonly routerLink: string; + readonly status: string; + readonly dossierName: string; + readonly numberOfPages: number; +}