diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts index 1f69460448..521cb9ee81 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts @@ -1092,3 +1092,123 @@ describe('parseHighlightFields', () => { }); }); }); + +describe('stopword testing', () => { + let testLunrSearchEngine: SearchEngine; + + beforeEach(() => { + testLunrSearchEngine = new LunrSearchEngine({ logger: getVoidLogger() }); + jest.clearAllMocks(); + }); + + it('test with stopword in title', async () => { + const mockDocuments = [ + { + title: 'a home', + text: 'mary had a little lamb at home', + location: 'test/location', + }, + ]; + + const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index'); + + await TestPipeline.withSubject(indexer) + .withDocuments(mockDocuments) + .execute(); + + const mockedSearchResult = await testLunrSearchEngine.query({ + term: 'mary', + filters: { + title: 'a home', + }, + }); + + expect(mockedSearchResult).toMatchObject({ + results: [ + { + document: { + title: 'a home', + text: 'mary had a little lamb at home', + location: 'test/location', + }, + rank: 1, + }, + ], + nextPageCursor: undefined, + }); + }); + + it('test with stopword connected with dash', async () => { + const mockDocuments = [ + { + title: 'a-home', + text: 'mary had a little lamb at home', + location: 'test/location', + }, + ]; + + const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index'); + + await TestPipeline.withSubject(indexer) + .withDocuments(mockDocuments) + .execute(); + + const mockedSearchResult = await testLunrSearchEngine.query({ + term: 'mary', + filters: { + title: 'a-home', + }, + }); + + expect(mockedSearchResult).toMatchObject({ + results: [ + { + document: { + title: 'a-home', + text: 'mary had a little lamb at home', + location: 'test/location', + }, + rank: 1, + }, + ], + nextPageCursor: undefined, + }); + }); + + it('test with only stop word in title', async () => { + const mockDocuments = [ + { + title: 'a', + text: 'mary had a little lamb at home', + location: 'test/location', + }, + ]; + + const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index'); + + await TestPipeline.withSubject(indexer) + .withDocuments(mockDocuments) + .execute(); + + const mockedSearchResult = await testLunrSearchEngine.query({ + term: 'mary', + filters: { + title: 'a', + }, + }); + + expect(mockedSearchResult).toMatchObject({ + results: [ + { + document: { + title: 'a', + text: 'mary had a little lamb at home', + location: 'test/location', + }, + rank: 1, + }, + ], + nextPageCursor: undefined, + }); + }); +}); diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts index ec8550d721..8780490dcc 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts @@ -114,10 +114,16 @@ export class LunrSearchEngine implements SearchEngine { // Require that the given field has the given value if (['string', 'number', 'boolean'].includes(typeof value)) { - q.term(lunr.tokenizer(value?.toString()), { - presence: lunr.Query.presence.REQUIRED, - fields: [field], - }); + q.term( + lunr + .tokenizer(value?.toString()) + .map(lunr.stopWordFilter) + .filter(element => element !== undefined), + { + presence: lunr.Query.presence.REQUIRED, + fields: [field], + }, + ); } else if (Array.isArray(value)) { // Illustrate how multi-value filters could work. // But warn that Lurn supports this poorly.