From 0de67c4d2ae21fb7ed891b93ea43dcef28946e32 Mon Sep 17 00:00:00 2001 From: Tomasz Szuba Date: Thu, 21 Oct 2021 13:54:44 +0200 Subject: [PATCH 1/3] Handle single-item array filter in Lunr search engine Signed-off-by: Tomasz Szuba --- .changeset/spicy-foxes-joke.md | 5 +++++ .../search-backend-node/src/engines/LunrSearchEngine.ts | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/spicy-foxes-joke.md diff --git a/.changeset/spicy-foxes-joke.md b/.changeset/spicy-foxes-joke.md new file mode 100644 index 0000000000..c1ed318f6d --- /dev/null +++ b/.changeset/spicy-foxes-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-node': patch +--- + +Handle special case when filter array has single value optimizing Lunr search behaviour. diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts index f22762cb9f..ea51ffe8e1 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts @@ -80,11 +80,17 @@ export class LunrSearchEngine implements SearchEngine { }); if (filters) { - Object.entries(filters).forEach(([field, value]) => { + Object.entries(filters).forEach(([field, fieldValue]) => { if (!q.allFields.includes(field)) { // Throw for unknown field, as this will be a non match throw new Error(`unrecognised field ${field}`); } + // Arrays are poorly supported, but we can make it better for single-item arrays, + // which should be a common case + const value = + Array.isArray(fieldValue) && fieldValue.length === 1 + ? fieldValue[0] + : fieldValue; // Require that the given field has the given value if (['string', 'number', 'boolean'].includes(typeof value)) { From 428061d7e56451750d02a9f8fcb2f6101338f347 Mon Sep 17 00:00:00 2001 From: Tomasz Szuba Date: Thu, 21 Oct 2021 14:04:58 +0200 Subject: [PATCH 2/3] Add tests Signed-off-by: Tomasz Szuba --- .../src/engines/LunrSearchEngine.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts index a2cbb4ecc3..cbe48ef5a7 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts @@ -192,6 +192,37 @@ describe('LunrSearchEngine', () => { }); }); + it('should handle single-item array filter as scalar value', async () => { + const inspectableSearchEngine = new LunrSearchEngineForTranslatorTests({ + logger: getVoidLogger(), + }); + const translatorUnderTest = inspectableSearchEngine.getTranslator(); + + const actualTranslatedQuery = translatorUnderTest({ + term: 'testTerm', + filters: { kind: ['testKind'] }, + }) as ConcreteLunrQuery; + + expect(actualTranslatedQuery).toMatchObject({ + documentTypes: undefined, + lunrQueryBuilder: expect.any(Function), + }); + + const query: jest.Mocked = { + allFields: ['kind'], + clauses: [], + term: jest.fn(), + clause: jest.fn(), + }; + + actualTranslatedQuery.lunrQueryBuilder.bind(query)(query); + + expect(query.term).toBeCalledWith(lunr.tokenizer('testKind'), { + fields: ['kind'], + presence: lunr.Query.presence.REQUIRED, + }); + }); + it('should return translated query with multiple filters', async () => { const inspectableSearchEngine = new LunrSearchEngineForTranslatorTests({ logger: getVoidLogger(), From a369f19e7eed5b40bff1b225cbe475c5ad170b0b Mon Sep 17 00:00:00 2001 From: Tomasz Szuba Date: Thu, 21 Oct 2021 15:13:30 +0200 Subject: [PATCH 3/3] Rename changeset Signed-off-by: Tomasz Szuba --- .changeset/{spicy-foxes-joke.md => search-spicy-foxes-joke.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changeset/{spicy-foxes-joke.md => search-spicy-foxes-joke.md} (100%) diff --git a/.changeset/spicy-foxes-joke.md b/.changeset/search-spicy-foxes-joke.md similarity index 100% rename from .changeset/spicy-foxes-joke.md rename to .changeset/search-spicy-foxes-joke.md