/* * Bewertung einer Anzeige gegen einen Suchagenten - ohne KI. * * Wunsch vom 29.09.2026: kein Abgleich mit Lebenslaeufen, sondern "je mehr * Schlagwoerter gefunden werden, desto besser der Treffer". Gezaehlt wird in * Titel und Text; ein Schlagwort im Titel zaehlt doppelt, weil der Titel die * Rolle benennt und der Text oft nur Umfeld beschreibt. * * score = Summe(Gewicht je gefundenem Schlagwort) / (2 * Anzahl Schlagwoerter) * Gewicht 2 = im Titel, 1 = nur im Text */ const normalize = (text) => String(text ?? '').normalize('NFC').toLowerCase(); const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); /** * Kurze Begriffe ("IT", "SAP", "HR") nur als ganzes Wort - sonst steckt "it" in * jedem "Mitarbeiter". Laengere als Teilwort, damit "Projektleiter" auch in * "IT-Projektleiterin" und "Projektleiterstelle" trifft. */ export function containsTerm(text, term) { const needle = normalize(term).trim(); if (!needle) return false; const haystack = normalize(text); if (needle.length <= 3) { return new RegExp(`(? containsTerm(all, term))) return null; const keywords = agent.keywords ?? []; if (keywords.length === 0) return null; let points = 0; const hits = []; for (const keyword of keywords) { if (containsTerm(title, keyword)) { points += 2; hits.push(keyword); } else if (containsTerm(all, keyword)) { points += 1; hits.push(keyword); } } if (hits.length === 0) return null; return { score: Math.round((100 * points) / (2 * keywords.length)), hits }; } /* * Firmennamen der Boersen und des CRM auf einen Nenner bringen: * "Müller Holding GmbH & Co. KG" -> "müller holding". */ const LEGAL_FORMS = /\b(gmbh|mbh|ag|se|kg|kgaa|ohg|gbr|ug|e\.?\s?v|e\.?\s?k|co|inc|ltd|llc|plc|sa|sarl|bv|nv|haftungsbeschränkt|gesellschaft mit beschränkter haftung|aktiengesellschaft)\b\.?/g; export function normalizeCompany(name) { return normalize(name) .replace(/\(.*?\)/g, ' ') .replace(/&/g, ' ') .replace(LEGAL_FORMS, ' ') .replace(/[^\p{L}\p{N}]+/gu, ' ') .replace(/\s+/g, ' ') .trim(); } /** * Sucht das Unternehmen im CRM. Gleicher Name gewinnt; sonst darf der Name der * Anzeige mit dem CRM-Namen beginnen ("Siemens" -> "Siemens Mobility"), aber * nur ab fuenf Zeichen, sonst wird aus "ABC" jeder, der mit "ABC" anfaengt. * * @param {Array<{id:number, key:string}>} companies vorberechnet, laengste zuerst */ export function findCompany(companies, name) { const key = normalizeCompany(name); if (!key) return null; const exact = companies.find((company) => company.key === key); if (exact) return exact.id; const prefix = companies.find((company) => company.key.length >= 5 && key.startsWith(`${company.key} `)); return prefix ? prefix.id : null; }