Make the QueryTranslator a more integral part of the SearchEngine API
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -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',
|
||||
});
|
||||
|
||||
@@ -33,6 +33,8 @@ type LunrResultEnvelope = {
|
||||
type: string;
|
||||
};
|
||||
|
||||
type LunrQueryTranslator = (query: SearchQuery) => ConcreteLunrQuery;
|
||||
|
||||
export class LunrSearchEngine implements SearchEngine {
|
||||
protected lunrIndices: Record<string, lunr.Index> = {};
|
||||
protected docStore: Record<string, IndexableDocument>;
|
||||
@@ -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.
|
||||
|
||||
@@ -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<SearchResultSet>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user