Feat: Serverseitige Fuzzy-Suche für /api/sounds und Client-Filter vereinfacht
This commit is contained in:
parent
6f51c493ed
commit
d975114768
2 changed files with 46 additions and 6 deletions
|
|
@ -485,7 +485,50 @@ app.get('/api/sounds', (req: Request, res: Response) => {
|
||||||
itemsByFolder = allItems.filter((it) => (folderFilter === '' ? it.folder === '' : it.folder === folderFilter));
|
itemsByFolder = allItems.filter((it) => (folderFilter === '' ? it.folder === '' : it.folder === folderFilter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const filteredItems = itemsByFolder.filter((s) => (q ? s.name.toLowerCase().includes(q) : true));
|
// Fuzzy-Score: bevorzugt Präfixe, zusammenhängende Treffer und frühe Positionen
|
||||||
|
function fuzzyScore(text: string, pattern: string): number {
|
||||||
|
if (!pattern) return 1;
|
||||||
|
if (text === pattern) return 2000;
|
||||||
|
const idx = text.indexOf(pattern);
|
||||||
|
if (idx !== -1) {
|
||||||
|
let base = 1000;
|
||||||
|
if (idx === 0) base += 200; // Präfix-Bonus
|
||||||
|
return base - idx * 2; // leichte Positionsstrafe
|
||||||
|
}
|
||||||
|
// subsequence Matching
|
||||||
|
let textIndex = 0;
|
||||||
|
let patIndex = 0;
|
||||||
|
let score = 0;
|
||||||
|
let lastMatch = -1;
|
||||||
|
let gaps = 0;
|
||||||
|
let firstMatchPos = -1;
|
||||||
|
while (textIndex < text.length && patIndex < pattern.length) {
|
||||||
|
if (text[textIndex] === pattern[patIndex]) {
|
||||||
|
if (firstMatchPos === -1) firstMatchPos = textIndex;
|
||||||
|
if (lastMatch === textIndex - 1) {
|
||||||
|
score += 5; // zusammenhängende Treffer belohnen
|
||||||
|
}
|
||||||
|
lastMatch = textIndex;
|
||||||
|
patIndex++;
|
||||||
|
} else if (firstMatchPos !== -1) {
|
||||||
|
gaps++;
|
||||||
|
}
|
||||||
|
textIndex++;
|
||||||
|
}
|
||||||
|
if (patIndex !== pattern.length) return 0; // nicht alle Pattern-Zeichen gefunden
|
||||||
|
score += Math.max(0, 300 - firstMatchPos * 2); // frühe Starts belohnen
|
||||||
|
score += Math.max(0, 100 - gaps * 10); // weniger Lücken belohnen
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteredItems = itemsByFolder;
|
||||||
|
if (q) {
|
||||||
|
const scored = itemsByFolder
|
||||||
|
.map((it) => ({ it, score: fuzzyScore(it.name.toLowerCase(), q) }))
|
||||||
|
.filter((x) => x.score > 0)
|
||||||
|
.sort((a, b) => (b.score - a.score) || a.it.name.localeCompare(b.it.name));
|
||||||
|
filteredItems = scored.map((x) => x.it);
|
||||||
|
}
|
||||||
|
|
||||||
const total = allItems.length;
|
const total = allItems.length;
|
||||||
const recentCount = Math.min(10, total);
|
const recentCount = Math.min(10, total);
|
||||||
|
|
|
||||||
|
|
@ -186,11 +186,8 @@ export default function App() {
|
||||||
})();
|
})();
|
||||||
}, [selected]);
|
}, [selected]);
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
// Server liefert bereits gefilterte (und ggf. fuzzy-sortierte) Ergebnisse
|
||||||
const q = query.trim().toLowerCase();
|
const filtered = sounds;
|
||||||
if (!q) return sounds;
|
|
||||||
return sounds.filter((s) => s.name.toLowerCase().includes(q));
|
|
||||||
}, [sounds, query]);
|
|
||||||
|
|
||||||
const favCount = useMemo(() => Object.values(favs).filter(Boolean).length, [favs]);
|
const favCount = useMemo(() => Object.values(favs).filter(Boolean).length, [favs]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue