From cd1acab4636bc3188130081dcd1228bb484f8014 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 9 Jun 2022 14:58:59 +0200 Subject: [PATCH 1/5] Document the `discover` event for search result clicks. Signed-off-by: Eric Peterson --- docs/plugins/analytics.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md index d5fd02a46d..ccb4e5c5a7 100644 --- a/docs/plugins/analytics.md +++ b/docs/plugins/analytics.md @@ -52,11 +52,12 @@ learn how to contribute the integration yourself! The following table summarizes events that, depending on the plugins you have installed, may be captured. -| Action | Provided By | Subject | -| ---------- | -------------- | --------------------------------------------------- | -| `navigate` | Backstage Core | The URL of the page that was navigated to | -| `click` | Backstage Core | The text of the link that was clicked on | -| `search` | Backstage Core | The search term entered in any search bar component | +| Action | Subject | Other Notes | +| ---------- | --------------------------------------------------- | ----------------------------------------------------------------- | +| `navigate` | The URL of the page that was navigated to | | +| `click` | The text of the link that was clicked on | The `to` attribute represents the URL clicked to | +| `search` | The search term entered in any search bar component | The `searchTypes` attribute holds `types` constraining the search | +| `discover` | The title of the search result that was clicked on | The `value` is the result rank. A `to` attribute is also provided | If there is an event you'd like to see captured, please [open an issue][add-event] describing the event you want to see and the questions it From 484afdf1dc7521a9865c82ee86f187628dd59472 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 9 Jun 2022 15:11:24 +0200 Subject: [PATCH 2/5] Add a rank attribute to the Result type. Signed-off-by: Eric Peterson --- .changeset/search-rank-and-file.md | 5 +++++ plugins/search-common/api-report.md | 4 +--- plugins/search-common/src/types.ts | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 .changeset/search-rank-and-file.md diff --git a/.changeset/search-rank-and-file.md b/.changeset/search-rank-and-file.md new file mode 100644 index 0000000000..bfad6a63ad --- /dev/null +++ b/.changeset/search-rank-and-file.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-common': patch +--- + +Added an optional `rank` attribute to the `Result` type. This represents the result rank (starting at 1) for a given result in a result set for a given search. diff --git a/plugins/search-common/api-report.md b/plugins/search-common/api-report.md index bb049742f7..ea9fc2d26e 100644 --- a/plugins/search-common/api-report.md +++ b/plugins/search-common/api-report.md @@ -52,11 +52,9 @@ export type QueryTranslator = (query: SearchQuery) => unknown; // @public (undocumented) export interface Result { - // (undocumented) document: TDocument; - // (undocumented) highlight?: ResultHighlight; - // (undocumented) + rank?: number; type: string; } diff --git a/plugins/search-common/src/types.ts b/plugins/search-common/src/types.ts index fdcc06287c..4b4c25d423 100644 --- a/plugins/search-common/src/types.ts +++ b/plugins/search-common/src/types.ts @@ -56,9 +56,27 @@ export interface ResultHighlight { * @public */ export interface Result { + /** + * The "type" of the given document. See: {@link DocumentCollatorFactory."type"} + */ type: string; + + /** + * The raw value of the document, as indexed. + */ document: TDocument; + + /** + * Optional result highlight. Useful for improving the search result + * display/experience. + */ highlight?: ResultHighlight; + + /** + * Optional result rank, where 1 is the first/top result returned. Useful for + * understanding search effectiveness in analytics. + */ + rank?: number; } /** From 915700f64fcf600ac0079571372944e8cb130f09 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 9 Jun 2022 15:17:55 +0200 Subject: [PATCH 3/5] Set a pagination-aware rank value for all search engines. Signed-off-by: Eric Peterson --- .changeset/search-rank-choice-voting.md | 8 ++++ .changeset/search-rankly-my-dear.md | 7 ++++ plugins/catalog/api-report.md | 2 + .../CatalogSearchResultListItem.tsx | 12 +++++- .../src/engines/ElasticSearchSearchEngine.ts | 41 ++++++++++--------- .../src/PgSearchEngine/PgSearchEngine.ts | 12 ++++-- .../src/engines/LunrSearchEngine.ts | 3 +- .../src/service/AuthorizedSearchEngine.ts | 13 ++++-- plugins/search-react/api-report.md | 1 + .../DefaultResultListItem.tsx | 14 ++++++- plugins/techdocs/api-report.md | 1 + .../TechDocsSearchResultListItem.tsx | 20 ++++++++- 12 files changed, 102 insertions(+), 32 deletions(-) create mode 100644 .changeset/search-rank-choice-voting.md create mode 100644 .changeset/search-rankly-my-dear.md diff --git a/.changeset/search-rank-choice-voting.md b/.changeset/search-rank-choice-voting.md new file mode 100644 index 0000000000..2754d149e7 --- /dev/null +++ b/.changeset/search-rank-choice-voting.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-search-backend': patch +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-search-backend-module-pg': patch +'@backstage/plugin-search-backend-node': patch +--- + +The provided search engine now adds a pagination-aware `rank` value to all results. diff --git a/.changeset/search-rankly-my-dear.md b/.changeset/search-rankly-my-dear.md new file mode 100644 index 0000000000..c528c567b1 --- /dev/null +++ b/.changeset/search-rankly-my-dear.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog': patch +'@backstage/plugin-search': patch +'@backstage/plugin-techdocs': patch +--- + +In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 5b7e07701c..34bb5dfcec 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -114,6 +114,8 @@ export interface CatalogSearchResultListItemProps { // (undocumented) icon?: ReactNode; // (undocumented) + rank?: number; + // (undocumented) result: IndexableDocument; } diff --git a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx index 993e34fbf6..a7307055c2 100644 --- a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx +++ b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx @@ -25,6 +25,7 @@ import { makeStyles, } from '@material-ui/core'; import { Link } from '@backstage/core-components'; +import { useAnalytics } from '@backstage/core-plugin-api'; import { IndexableDocument, ResultHighlight, @@ -51,6 +52,7 @@ export interface CatalogSearchResultListItemProps { icon?: ReactNode; result: IndexableDocument; highlight?: ResultHighlight; + rank?: number; } /** @public */ @@ -60,8 +62,16 @@ export function CatalogSearchResultListItem( const result = props.result as any; const classes = useStyles(); + const analytics = useAnalytics(); + const handleClick = () => { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: props.rank, + }); + }; + return ( - + {props.icon && {props.icon}}
diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index d11d8a1f90..31112e3446 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -334,27 +334,30 @@ export class ElasticSearchSearchEngine implements SearchEngine { : undefined; return { - results: result.body.hits.hits.map((d: ElasticSearchResult) => { - const resultItem: IndexableResult = { - type: this.getTypeFromIndex(d._index), - document: d._source, - }; - - if (d.highlight) { - resultItem.highlight = { - preTag: this.highlightOptions.preTag as string, - postTag: this.highlightOptions.postTag as string, - fields: Object.fromEntries( - Object.entries(d.highlight).map(([field, fragments]) => [ - field, - fragments.join(this.highlightOptions.fragmentDelimiter), - ]), - ), + results: result.body.hits.hits.map( + (d: ElasticSearchResult, index: number) => { + const resultItem: IndexableResult = { + type: this.getTypeFromIndex(d._index), + document: d._source, + rank: pageSize * page + index + 1, }; - } - return resultItem; - }), + if (d.highlight) { + resultItem.highlight = { + preTag: this.highlightOptions.preTag as string, + postTag: this.highlightOptions.postTag as string, + fields: Object.fromEntries( + Object.entries(d.highlight).map(([field, fragments]) => [ + field, + fragments.join(this.highlightOptions.fragmentDelimiter), + ]), + ), + }; + } + + return resultItem; + }, + ), nextPageCursor, previousPageCursor, }; diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts index 445d1bb5c1..bf98c58de9 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts @@ -18,6 +18,7 @@ import { SearchEngine } from '@backstage/plugin-search-backend-node'; import { SearchQuery, IndexableResultSet, + IndexableResult, } from '@backstage/plugin-search-common'; import { PgSearchEngineIndexer } from './PgSearchEngineIndexer'; import { @@ -104,10 +105,13 @@ export class PgSearchEngine implements SearchEngine { ? encodePageCursor({ page: page - 1 }) : undefined; - const results = pageRows.map(({ type, document }) => ({ - type, - document, - })); + const results = pageRows.map( + ({ type, document }, index): IndexableResult => ({ + type, + document, + rank: page * pageSize + index + 1, + }), + ); return { results, nextPageCursor, previousPageCursor }; } diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts index 4a5f4cdd18..03cb421653 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts @@ -207,9 +207,10 @@ export class LunrSearchEngine implements SearchEngine { // Translate results into IndexableResultSet const realResultSet: IndexableResultSet = { - results: results.slice(offset, offset + pageSize).map(d => ({ + results: results.slice(offset, offset + pageSize).map((d, index) => ({ type: d.type, document: this.docStore[d.result.ref], + rank: page * pageSize + index + 1, highlight: { preTag: this.highlightPreTag, postTag: this.highlightPostTag, diff --git a/plugins/search-backend/src/service/AuthorizedSearchEngine.ts b/plugins/search-backend/src/service/AuthorizedSearchEngine.ts index a74cdf8eef..ade43280be 100644 --- a/plugins/search-backend/src/service/AuthorizedSearchEngine.ts +++ b/plugins/search-backend/src/service/AuthorizedSearchEngine.ts @@ -189,10 +189,15 @@ export class AuthorizedSearchEngine implements SearchEngine { ); return { - results: filteredResults.slice( - page * this.pageSize, - (page + 1) * this.pageSize, - ), + results: filteredResults + .slice(page * this.pageSize, (page + 1) * this.pageSize) + .map((result, index) => { + // Overwrite any/all rank entries to avoid leaking knowledge of filtered results. + return { + ...result, + rank: page * this.pageSize + index + 1, + }; + }), previousPageCursor: page === 0 ? undefined : encodePageCursor({ page: page - 1 }), nextPageCursor: diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index f9bb993fd6..e4afee45ca 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -38,6 +38,7 @@ export type DefaultResultListItemProps = { secondaryAction?: ReactNode; result: SearchDocument; highlight?: ResultHighlight; + rank?: number; lineClamp?: number; }; diff --git a/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx index ff6f3d7fe1..f4260525d6 100644 --- a/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx +++ b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx @@ -15,7 +15,7 @@ */ import React, { ReactNode } from 'react'; -import { AnalyticsContext } from '@backstage/core-plugin-api'; +import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api'; import { ResultHighlight, SearchDocument, @@ -40,6 +40,7 @@ export type DefaultResultListItemProps = { secondaryAction?: ReactNode; result: SearchDocument; highlight?: ResultHighlight; + rank?: number; lineClamp?: number; }; @@ -51,12 +52,21 @@ export type DefaultResultListItemProps = { export const DefaultResultListItemComponent = ({ result, highlight, + rank, icon, secondaryAction, lineClamp = 5, }: DefaultResultListItemProps) => { + const analytics = useAnalytics(); + const handleClick = () => { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: rank, + }); + }; + return ( - + {icon && {icon}} { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: rank, + }); + }; + const TextItem = () => { const resultTitle = highlight?.fields.title ? ( ) => - asLink ? {children} : <>{children}; + asLink ? ( + + {children} + + ) : ( + <>{children} + ); const ListItemWrapper = ({ children }: PropsWithChildren<{}>) => asListItem ? ( From f7f5a6c6a3b0f925279dbe56bef2bfc80bdc3026 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 9 Jun 2022 15:51:32 +0200 Subject: [PATCH 4/5] Update the app and create-app to pass through rank Signed-off-by: Eric Peterson --- .changeset/search-pull-rank.md | 50 +++++++++++++++++++ .../app/src/components/search/SearchModal.tsx | 5 +- .../app/src/components/search/SearchPage.tsx | 5 +- .../app/src/components/search/SearchPage.tsx | 5 +- 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 .changeset/search-pull-rank.md diff --git a/.changeset/search-pull-rank.md b/.changeset/search-pull-rank.md new file mode 100644 index 0000000000..995225698e --- /dev/null +++ b/.changeset/search-pull-rank.md @@ -0,0 +1,50 @@ +--- +'@backstage/create-app': patch +--- + +It's now possible to pass result item components a `rank`, which is captured by the analytics API when a user clicks on a search result. To apply this change, update your `/packages/app/src/components/search/SearchPage.tsx` in the following way: + +```diff +// ... + + {({ results }) => ( + +- {results.map(({ type, document, highlight }) => { ++ {results.map(({ type, document, highlight, rank }) => { + switch (type) { + case 'software-catalog': + return ( + + ); + case 'techdocs': + return ( + + ); + default: + return ( + + ); + } + })} + + )} + +// ... +``` + +If you have implemented a custom Search Modal or other custom search experience, you will want to make similar changes in those components. diff --git a/packages/app/src/components/search/SearchModal.tsx b/packages/app/src/components/search/SearchModal.tsx index f5f4204302..99fc964cb2 100644 --- a/packages/app/src/components/search/SearchModal.tsx +++ b/packages/app/src/components/search/SearchModal.tsx @@ -184,7 +184,7 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => { {({ results }) => ( - {results.map(({ type, document, highlight }) => { + {results.map(({ type, document, highlight, rank }) => { let resultItem; switch (type) { case 'software-catalog': @@ -194,6 +194,7 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); break; @@ -204,6 +205,7 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); break; @@ -213,6 +215,7 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); } diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 9a2e85bd8c..053fa81da8 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -132,7 +132,7 @@ const SearchPage = () => { {({ results }) => ( - {results.map(({ type, document, highlight }) => { + {results.map(({ type, document, highlight, rank }) => { switch (type) { case 'software-catalog': return ( @@ -141,6 +141,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); case 'techdocs': @@ -150,6 +151,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); default: @@ -158,6 +160,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); } diff --git a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx index 1b3bf5b77d..928b8201ea 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx @@ -112,7 +112,7 @@ const SearchPage = () => { {({ results }) => ( - {results.map(({ type, document, highlight }) => { + {results.map(({ type, document, highlight, rank }) => { switch (type) { case 'software-catalog': return ( @@ -120,6 +120,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); case 'techdocs': @@ -128,6 +129,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); default: @@ -136,6 +138,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); } From c7ce7b1eb043c4d5d2a701f32380dabc0e6493c6 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 9 Jun 2022 17:26:28 +0200 Subject: [PATCH 5/5] Test rank handling in all search engines. Signed-off-by: Eric Peterson --- .../engines/ElasticSearchSearchEngine.test.ts | 4 ++++ .../src/PgSearchEngine/PgSearchEngine.test.ts | 3 +++ .../src/engines/LunrSearchEngine.test.ts | 14 +++++++++++++ .../service/AuthorizedSearchEngine.test.ts | 20 +++++++++++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts index c114406d99..7892b731ed 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts @@ -523,6 +523,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, })), ), }); @@ -567,6 +568,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, })), ), nextPageCursor: 'MQ==', @@ -614,6 +616,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, })) .slice(25), ), @@ -667,6 +670,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, highlight: { preTag: '', postTag: '', diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts index cc89f28c80..3ed002567e 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts @@ -174,6 +174,7 @@ describe('PgSearchEngine', () => { location: 'location-1', }, type: 'my-type', + rank: 1, }, ], nextPageCursor: undefined, @@ -215,6 +216,7 @@ describe('PgSearchEngine', () => { location: `location-${i}`, }, type: 'my-type', + rank: i + 1, })), nextPageCursor: 'MQ==', }); @@ -257,6 +259,7 @@ describe('PgSearchEngine', () => { location: `location-${i}`, }, type: 'my-type', + rank: i + 1, })) .slice(25), previousPageCursor: 'MA==', diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts index 84b1a35165..098d1e8b16 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts @@ -431,6 +431,7 @@ describe('LunrSearchEngine', () => { location: 'test/location', }, type: 'test-index', + rank: 1, }, ], nextPageCursor: undefined, @@ -469,6 +470,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -521,6 +523,7 @@ describe('LunrSearchEngine', () => { location: `${highlightTags.pre}test/location${highlightTags.post}`, }, }, + rank: 1, }, ], nextPageCursor: undefined, @@ -559,6 +562,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -598,6 +602,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -637,6 +642,7 @@ describe('LunrSearchEngine', () => { text: 'Hello World.', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -676,6 +682,7 @@ describe('LunrSearchEngine', () => { text: 'Searching', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -722,6 +729,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location2', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -777,6 +785,7 @@ describe('LunrSearchEngine', () => { location: 'test/location2', extraField: 'testExtraField', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -823,6 +832,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test:location2', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -886,6 +896,7 @@ describe('LunrSearchEngine', () => { text: 'testText', title: 'testTitle', }, + rank: 1, }, { document: { @@ -893,6 +904,7 @@ describe('LunrSearchEngine', () => { text: 'testText', title: 'testTitle', }, + rank: 2, }, ], nextPageCursor: undefined, @@ -931,6 +943,7 @@ describe('LunrSearchEngine', () => { location: `test/location/${i}`, }, type: 'test-index', + rank: i + 1, })), nextPageCursor: 'MQ==', previousPageCursor: undefined, @@ -968,6 +981,7 @@ describe('LunrSearchEngine', () => { location: `test/location/${i}`, }, type: 'test-index', + rank: i + 1, })) .slice(25), nextPageCursor: undefined, diff --git a/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts b/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts index 256608fd82..b2355a7239 100644 --- a/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts +++ b/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts @@ -44,6 +44,7 @@ describe('AuthorizedSearchEngine', () => { .fill(0) .map((_, index) => ({ type, + rank: index + 1, document: { title: `${type}_doc_${index}`, authorization: withAuthorization @@ -261,9 +262,11 @@ describe('AuthorizedSearchEngine', () => { }), ); + const expectedResult = { ...usersWithAuth[8], rank: 1 }; + await expect( authorizedSearchEngine.query({ term: '' }, options), - ).resolves.toEqual({ results: [usersWithAuth[8]] }); + ).resolves.toEqual({ results: [expectedResult] }); expect(mockedQuery).toHaveBeenCalledWith( { term: '', types: ['users'] }, @@ -275,6 +278,7 @@ describe('AuthorizedSearchEngine', () => { const searchResults = [ { type: 'templates', + rank: 1, document: { title: `doc_0_a`, authorization: { resourceRef: `template_doc_0` }, @@ -282,6 +286,7 @@ describe('AuthorizedSearchEngine', () => { }, { type: 'templates', + rank: 2, document: { title: `doc_0_b`, authorization: { resourceRef: `template_doc_0` }, @@ -419,7 +424,9 @@ describe('AuthorizedSearchEngine', () => { { token: 'token' }, ); - const expectedResult = allDocuments.slice(0, 25); + const expectedResult = allDocuments + .slice(0, 25) + .map((r, i) => ({ ...r, rank: i + 1 })); const expectedFirstRequestCursor = 'MQ=='; expect(result).toEqual({ @@ -510,7 +517,8 @@ describe('AuthorizedSearchEngine', () => { const expectedResult = allDocuments .filter(d => d.type !== typeServices) - .slice(0, 25); + .slice(0, 25) + .map((d, i) => ({ ...d, rank: i + 1 })); const expectedFirstRequestCursor = 'MQ=='; expect(result).toEqual({ @@ -587,8 +595,12 @@ describe('AuthorizedSearchEngine', () => { { token: 'token' }, ); + const expectedResults = servicesWithAuth + .slice(5) + .map((r, i) => ({ ...r, rank: 25 + i + 1 })); + expect(result).toEqual({ - results: servicesWithAuth.slice(5), + results: expectedResults, previousPageCursor: encodePageCursor({ page: 0 }), }); });