From 2aaa8e5cac11f3336bee5688563bc9479c1057ce Mon Sep 17 00:00:00 2001 From: Iain Date: Fri, 13 Nov 2026 04:20:44 +0000 Subject: [PATCH] fix(search): find a model number typed after the brand or name --- src/lib/search.js | 23 ++++++++++++++++++++++- tests/search.test.js | 7 +++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/lib/search.js b/src/lib/search.js index 5ed2f14..d9388ce 100644 --- a/src/lib/search.js +++ b/src/lib/search.js @@ -132,12 +132,33 @@ function fit(word, { words, otherWords, codes }) { return word.length > 3 && words.some((own) => nearly(word, own)) ? 1 : 0; } +const total = (fits) => fits.reduce((sum, points) => sum + points, 0); + +/** Where a code sits in the words, as "Galaxy SM-G991B" splits into galaxy sm g 991 b */ +function codeRun(words, codes) { + for (let from = 0; from < words.length; from++) { + for (let to = words.length; to > from + 1; to--) { + const run = words.slice(from, to).join(''); + const tail = /^[a-z]/.test(run) && run.length > 3; + if (/\d/.test(run) && codes.some((code) => code === run || (tail && code.endsWith(run)))) { + return [from, to]; + } + } + } +} + /** Every word has to fit, unless the whole query is the device's name or a code run together */ function score(words, device) { const whole = words.join(''); if (device.whole === whole || device.codes.includes(whole)) return 100; const fits = words.map((word) => fit(word, device)); - if (fits.every(Boolean)) return fits.reduce((sum, points) => sum + points, 0); + if (fits.every(Boolean)) return total(fits); + const run = codeRun(words, device.codes); + if (run) { + const rest = [...words.slice(0, run[0]), ...words.slice(run[1])]; + const restFits = rest.map((word) => fit(word, device)); + if (restFits.every(Boolean)) return 50 + total(restFits); + } return whole.length > 3 && device.codes.some((code) => code.includes(whole)) ? 5 : 0; } diff --git a/tests/search.test.js b/tests/search.test.js index faa9cc4..1ab83ad 100644 --- a/tests/search.test.js +++ b/tests/search.test.js @@ -68,6 +68,13 @@ test('codenames and model numbers find their phone, however they are typed', () } }); +test('a model number still finds its phone with the brand or name in front', () => { + for (const query of ['samsung SM-G991B', 'galaxy s21 SM-G991B', 'samsung g991b']) { + assert.deepEqual(names(query), ['Samsung Galaxy S21']); + } + assert.deepEqual(names('pixel SM-G991B'), []); +}); + test('a ROM or feature in the query keeps only the phones where it fits', () => { assert.deepEqual(names('pixel lineage'), ['Google Pixel 7', 'Google Pixel 7 Pro']); assert.deepEqual(names('pixel volte'), ['Google Pixel 7', 'Google Pixel 7a']);