Match search words at the start of a word, not anywhere inside one
Searching "Winkler H" returned all seven Winklers instead of the one Hannah. Each word was matched as a substring, so "H" hit T-h-omas, Kat-h-arina and CNC-Dre-h-er:in — every row. The shorter the input, the more useless the result, and an initial is the shortest input anyone would type. A word now has to match at the start of a word: either the haystack begins with it, or a space does. The haystack is first name, last name and job title joined, with hyphens, slashes, colons and dots flattened to spaces, so "dreher" still finds CNC-Dreher:in and "cnc" still finds both the Dreher and the Fräser. Checked against the live data before and after: "winkler h" now returns Hannah Winkler alone, "h winkler" the same in either order, "winkler kat" the two Katharinas, "dreher" the twelve CNC-Dreher. The trigram index on the concatenated name no longer applies, which is the price. At under nine hundred rows the scan is a few milliseconds; an index on the same expression brings it back when that stops being true. LIKE's own wildcards are escaped now — typing "100%" searched for everything before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
35
lib/employee-search.ts
Normal file
35
lib/employee-search.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// Wie aus einer Eingabe Suchmuster werden.
|
||||
//
|
||||
// Getrennt von der Seite, weil hier die Entscheidungen stecken, die man
|
||||
// prüfen können muss: was als Wort zählt, was am Wortanfang treffen muss und
|
||||
// was von LIKE als Text und nicht als Platzhalter gelesen wird.
|
||||
//
|
||||
// Die Bedeutung selbst — „trifft am Wortanfang" — liegt im SQL der Seite:
|
||||
// verglichen wird gegen Vorname, Nachname und Position zusammengesetzt, mit
|
||||
// Trennzeichen als Wortgrenze.
|
||||
|
||||
/** Nur Ziffern? Dann ist es eine Personalnummer und kein Name. */
|
||||
export function istPersonalnummer(term: string): boolean {
|
||||
return /^\d+$/.test(term.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Je Suchwort zwei LIKE-Muster: „am Anfang" und „nach einem Leerzeichen".
|
||||
* Zusammen ergeben sie „am Anfang eines Wortes".
|
||||
*
|
||||
* Ein Wort als Teilzeichenkette zu suchen wäre einfacher und war die erste
|
||||
* Fassung — bei „Winkler H" traf das „H" dann auf Thomas, Katharina und
|
||||
* CNC-Dreher:in, also auf alle. Je kürzer die Eingabe, desto unbrauchbarer
|
||||
* das Ergebnis, und ein Anfangsbuchstabe ist die kürzeste sinnvolle Eingabe.
|
||||
*/
|
||||
export function suchMuster(term: string): string[][] {
|
||||
return term
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.map((wort) => {
|
||||
// Die Platzhalter von LIKE entschärfen: wer „50 %" tippt, sucht Text.
|
||||
const klein = wort.toLowerCase().replace(/[\\%_]/g, (z) => `\\${z}`);
|
||||
return [`${klein}%`, `% ${klein}%`];
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user