From 8cb45d747a3e149217161f935ff4987d701154db Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Tue, 1 Jun 2021 11:48:04 +0200 Subject: [PATCH] Make the QueryTranslator a more integral part of the SearchEngine API Signed-off-by: Eric Peterson --- .../src/engines/LunrSearchEngine.test.ts | 49 +++++++++++++++---- .../src/engines/LunrSearchEngine.ts | 8 ++- plugins/search-backend-node/src/types.ts | 13 ++++- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts index f597327dbf..969e579a53 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts @@ -18,6 +18,15 @@ import { getVoidLogger } from '@backstage/backend-common'; import { LunrSearchEngine } from './LunrSearchEngine'; import { SearchEngine } from '../types'; +/** + * Just used to test the default translator shipped with LunrSearchEngine. + */ +class LunrSearchEngineForTranslatorTests extends LunrSearchEngine { + getTranslator() { + return this.translator; + } +} + describe('LunrSearchEngine', () => { let testLunrSearchEngine: SearchEngine; @@ -27,16 +36,21 @@ describe('LunrSearchEngine', () => { describe('translator', () => { it('query translator invoked', async () => { - const translatorSpy = jest.spyOn(testLunrSearchEngine, 'translator'); + // Given: Set a translator spy on the search engine. + const translatorSpy = jest.fn().mockReturnValue({ + lunrQueryString: '', + documentTypes: [], + }); + testLunrSearchEngine.setTranslator(translatorSpy); - // Translate query and ensure the translator was invoked. - await testLunrSearchEngine.translator({ + // When: querying the search engine + testLunrSearchEngine.query({ term: 'testTerm', filters: {}, pageCursor: '', }); - expect(translatorSpy).toHaveBeenCalled(); + // Then: the translator is invoked with expected args. expect(translatorSpy).toHaveBeenCalledWith({ term: 'testTerm', filters: {}, @@ -45,39 +59,54 @@ describe('LunrSearchEngine', () => { }); it('should return translated query', async () => { - const mockedTranslatedQuery = await testLunrSearchEngine.translator({ + const inspectableSearchEngine = new LunrSearchEngineForTranslatorTests({ + logger: getVoidLogger(), + }); + const translatorUnderTest = inspectableSearchEngine.getTranslator(); + + const actualTranslatedQuery = translatorUnderTest({ term: 'testTerm', filters: {}, pageCursor: '', }); - expect(mockedTranslatedQuery).toMatchObject({ + expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['*'], lunrQueryString: '+testTerm', }); }); it('should return translated query with 1 filter', async () => { - const mockedTranslatedQuery = await testLunrSearchEngine.translator({ + const inspectableSearchEngine = new LunrSearchEngineForTranslatorTests({ + logger: getVoidLogger(), + }); + const translatorUnderTest = inspectableSearchEngine.getTranslator(); + + const actualTranslatedQuery = translatorUnderTest({ term: 'testTerm', filters: { kind: 'testKind' }, pageCursor: '', }); - expect(mockedTranslatedQuery).toMatchObject({ + expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['*'], lunrQueryString: '+testTerm +kind:testKind', }); }); it('should return translated query with multiple filters', async () => { - const mockedTranslatedQuery = await testLunrSearchEngine.translator({ + const inspectableSearchEngine = new LunrSearchEngineForTranslatorTests({ + logger: getVoidLogger(), + }); + const translatorUnderTest = inspectableSearchEngine.getTranslator(); + + const actualTranslatedQuery = translatorUnderTest({ term: 'testTerm', filters: { kind: 'testKind', namespace: 'testNameSpace' }, pageCursor: '', }); - expect(mockedTranslatedQuery).toMatchObject({ + expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['*'], lunrQueryString: '+testTerm +kind:testKind +namespace:testNameSpace', }); diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts index d6afdc21ae..be51738920 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts @@ -33,6 +33,8 @@ type LunrResultEnvelope = { type: string; }; +type LunrQueryTranslator = (query: SearchQuery) => ConcreteLunrQuery; + export class LunrSearchEngine implements SearchEngine { protected lunrIndices: Record = {}; protected docStore: Record; @@ -43,7 +45,7 @@ export class LunrSearchEngine implements SearchEngine { this.docStore = {}; } - translator: QueryTranslator = ({ + protected translator: QueryTranslator = ({ term, filters, types, @@ -82,6 +84,10 @@ export class LunrSearchEngine implements SearchEngine { }; }; + setTranslator(translator: LunrQueryTranslator) { + this.translator = translator; + } + index(type: string, documents: IndexableDocument[]): void { const lunrBuilder = new lunr.Builder(); // Make this lunr index aware of all relevant fields. diff --git a/plugins/search-backend-node/src/types.ts b/plugins/search-backend-node/src/types.ts index b47c191a7c..e7643ee5aa 100644 --- a/plugins/search-backend-node/src/types.ts +++ b/plugins/search-backend-node/src/types.ts @@ -59,7 +59,18 @@ export type QueryTranslator = (query: SearchQuery) => unknown; * concrete, search engine-specific queries. */ export interface SearchEngine { - translator: QueryTranslator; + /** + * Override the default translator provided by the SearchEngine. + */ + setTranslator(translator: QueryTranslator): void; + + /** + * Add the given documents to the SearchEngine index of the given type. + */ index(type: string, documents: IndexableDocument[]): void; + + /** + * Perform a search query against the SearchEngine. + */ query(query: SearchQuery): Promise; }