From 0c699031236f61545625a4f086554e49d61b2380 Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Sun, 30 Apr 2017 16:33:38 -0700 Subject: [PATCH] feat(aio): top 5 weighted search results shown when many area results --- .../search-results.component.html | 6 +- .../search-results.component.spec.ts | 82 ++++++++++++------- .../search-results.component.ts | 11 ++- 3 files changed, 66 insertions(+), 33 deletions(-) diff --git a/aio/src/app/search/search-results/search-results.component.html b/aio/src/app/search/search-results/search-results.component.html index b801b1afa9..db05a09d27 100644 --- a/aio/src/app/search/search-results/search-results.component.html +++ b/aio/src/app/search/search-results/search-results.component.html @@ -1,8 +1,12 @@

Search Results

-

{{area.name}}

+

{{area.name}} ({{area.pages.length}})

    +
  • + {{ page.title }} +
  • +

  • {{ page.title }}
  • diff --git a/aio/src/app/search/search-results/search-results.component.spec.ts b/aio/src/app/search/search-results/search-results.component.spec.ts index bc460cdc2a..f28eb376ff 100644 --- a/aio/src/app/search/search-results/search-results.component.spec.ts +++ b/aio/src/app/search/search-results/search-results.component.spec.ts @@ -16,6 +16,25 @@ describe('SearchResultsComponent', () => { /** Get all text from component element */ function getText() { return fixture.debugElement.nativeElement.innerText; } + /** Get a full set of test results. "Take" what you need */ + function getTestResults(take?: number) { + const results: SearchResult[] = [ + { path: 'guide/a', title: 'Guide A'}, + { path: 'api/d', title: 'API D'}, + { path: 'guide/b', title: 'Guide B' }, + { path: 'guide/a/c', title: 'Guide A - C'}, + { path: 'api/c', title: 'API C' } + ] + // fill it out to exceed 10 guide pages + .concat('nmlkjihgfe'.split('').map(l => { + return { path: 'guide/' + l, title: 'Guide ' + l}; + })) + // add these empty fields to satisfy interface + .map(r => ({...r, ...{ keywords: '', titleWords: '', type: '' }})); + + return take === undefined ? results : results.slice(0, take); + } + beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ SearchResultsComponent ], @@ -36,50 +55,57 @@ describe('SearchResultsComponent', () => { }); it('should map the search results into groups based on their containing folder', () => { - const results = [ - {path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' }, - {path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' }, - {path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' }, - {path: 'guide/b/c', title: 'Guide B - C', type: 'content', keywords: '', titleWords: '' }, - ]; + const results = getTestResults(3); searchResults.next({ query: '', results: results}); expect(currentAreas).toEqual([ { name: 'api', pages: [ - { path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' } - ] }, + { path: 'api/d', title: 'API D', type: '', keywords: '', titleWords: '' } + ], priorityPages: [] }, { name: 'guide', pages: [ - { path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' }, - { path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' }, - { path: 'guide/b/c', title: 'Guide B - C', type: 'content', keywords: '', titleWords: '' } - ] } + { path: 'guide/a', title: 'Guide A', type: '', keywords: '', titleWords: '' }, + { path: 'guide/b', title: 'Guide B', type: '', keywords: '', titleWords: '' }, + ], priorityPages: [] } ]); }); it('should sort by title within sorted area', () => { - const results = [ - {path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' }, - {path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' }, - {path: 'api/d', title: 'API D', type: 'class', keywords: '', titleWords: '' }, - {path: 'guide/a/c', title: 'Guide A - C', type: 'content', keywords: '', titleWords: '' }, - {path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' }, - ]; - + const results = getTestResults(5); searchResults.next({ query: '', results: results }); expect(currentAreas).toEqual([ { name: 'api', pages: [ - {path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' }, - {path: 'api/d', title: 'API D', type: 'class', keywords: '', titleWords: '' }, - ] }, + {path: 'api/c', title: 'API C', type: '', keywords: '', titleWords: '' }, + {path: 'api/d', title: 'API D', type: '', keywords: '', titleWords: '' }, + ], priorityPages: [] }, { name: 'guide', pages: [ - {path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' }, - {path: 'guide/a/c', title: 'Guide A - C', type: 'content', keywords: '', titleWords: '' }, - {path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' }, - ] } + {path: 'guide/a', title: 'Guide A', type: '', keywords: '', titleWords: '' }, + {path: 'guide/a/c', title: 'Guide A - C', type: '', keywords: '', titleWords: '' }, + {path: 'guide/b', title: 'Guide B', type: '', keywords: '', titleWords: '' }, + ], priorityPages: [] } ]); }); + it('should put first 5 area results into priorityPages when more than 10 pages', () => { + const results = getTestResults(); + const sorted = results.slice().sort((l, r) => l.title > r.title ? 1 : -1); + const expected = [ + { + name: 'api', + pages: sorted.filter(p => p.path.startsWith('api')), + priorityPages: [] + }, + { + name: 'guide', + pages: sorted.filter(p => p.path.startsWith('guide')), + priorityPages: results.filter(p => p.path.startsWith('guide')).slice(0, 5) + } + ]; + + searchResults.next({ query: '', results: results }); + expect(currentAreas).toEqual(expected); + }); + it('should put search results with no containing folder into the default area (other)', () => { const results = [ {path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' } @@ -89,7 +115,7 @@ describe('SearchResultsComponent', () => { expect(currentAreas).toEqual([ { name: 'other', pages: [ { path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' } - ] } + ], priorityPages: [] } ]); }); diff --git a/aio/src/app/search/search-results/search-results.component.ts b/aio/src/app/search/search-results/search-results.component.ts index 5736dd2703..86423c3a98 100644 --- a/aio/src/app/search/search-results/search-results.component.ts +++ b/aio/src/app/search/search-results/search-results.component.ts @@ -6,6 +6,7 @@ import { SearchResult, SearchResults, SearchService } from '../search.service'; export interface SearchArea { name: string; pages: SearchResult[]; + priorityPages: SearchResult[]; } /** @@ -65,10 +66,12 @@ export class SearchResultsComponent implements OnInit { area.push(result); }); const keys = Object.keys(searchAreaMap).sort((l, r) => l > r ? 1 : -1); - return keys.map(name => ({ - name, - pages: searchAreaMap[name].sort(compareResults) - })); + return keys.map(name => { + let pages = searchAreaMap[name]; + const priorityPages = pages.length > 10 ? searchAreaMap[name].slice(0, 5) : []; + pages = pages.sort(compareResults); + return { name, pages, priorityPages }; + }); } // Split the search result path and use the top level folder, if there is one, as the area name.