From fc00d3861cb6906c7c4111f6c828da8e1890df9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 30 Jul 2026 02:11:09 +0000 Subject: [PATCH] soulseek: match share filter word by word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A folder's name is rarely something you can type in full — you remember the band and one word of the album, not the year, the format tag or where the apostrophe went. Whole-string matching made those two halves useless together: "pogues hell" matched nothing, because no path has them adjacent. Each word is now its own substring test, AND-ed and order-independent, so the two ends you do remember narrow the tree between them. Co-Authored-By: Claude Opus 4.8 --- src/databases/officer_db/src/queries/soulseek.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/databases/officer_db/src/queries/soulseek.ts b/src/databases/officer_db/src/queries/soulseek.ts index cc123151..e86e21a5 100644 --- a/src/databases/officer_db/src/queries/soulseek.ts +++ b/src/databases/officer_db/src/queries/soulseek.ts @@ -290,9 +290,15 @@ export async function searchSoulseekBrowseTree(params: BrowseTreeSearchParams): const term = q.trim(); if (snapshotId === null || !term) return { nodes: [], matched: [], total: 0, truncated: false }; + // Words are AND-ed, each matched anywhere in the path, so 'pogues hell' finds + // '…\The Pogues\… - Hell's Ditch …' — a folder you can name from two ends but never type in full. // ILIKE with a leading wildcard can't use the btree index, so this was already a scan of the snapshot's - // ~18k rows — which is why folding the column here costs nothing on top. - const where = and(eq(soulseekBrowseDirs.snapshotId, snapshotId), sql`${foldedName} ilike ${likePattern(term)}`); + // ~18k rows — which is why folding the column, once per word, costs nothing on top. + const words = term.split(/\s+/).filter(Boolean); + const where = and( + eq(soulseekBrowseDirs.snapshotId, snapshotId), + ...words.map((word) => sql`${foldedName} ilike ${likePattern(word)}`), + ); const [matches, [count]] = await Promise.all([ db.select(treeCols).from(soulseekBrowseDirs).where(where).orderBy(asc(soulseekBrowseDirs.name)).limit(limit),