fix: классификатор не ловил кириллицу и склонения
- regex `\b` в JS не работает с кириллицей (русские буквы не word-char), поэтому ни одно русское ключевое слово не давало хитов; - exact-match не учитывал склонения: «приложение» vs «приложении». Заменил на ручной матч с допуском в 2 символа суффикса (shared prefix ≥ max(keyword.length - 2, 4)). Сохранён exact-match для коротких ключей (sms/ios/crm) чтобы избежать ложных срабатываний. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d8f3cfea24
commit
0455123d8e
42
index.html
42
index.html
@ -448,23 +448,41 @@ function applyLang(){
|
||||
function normalize(s){
|
||||
return s.toLowerCase().replace(/ё/g,"е").replace(/[^\p{L}\p{N}\s]/gu," ").split(/\s+/).filter(Boolean);
|
||||
}
|
||||
function escapeReg(s){ return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"); }
|
||||
function matchTerm(tokens, term){
|
||||
const norm = normalize(term);
|
||||
if (norm.length === 0) return 0;
|
||||
if (norm.length > 1) {
|
||||
const joined = " " + tokens.join(" ") + " ";
|
||||
const needle = " " + norm.join(" ") + " ";
|
||||
let cnt = 0, idx = 0;
|
||||
while ((idx = joined.indexOf(needle, idx)) !== -1) { cnt++; idx += needle.length - 1; }
|
||||
return cnt;
|
||||
}
|
||||
const n = norm[0];
|
||||
let cnt = 0;
|
||||
for (const tok of tokens) {
|
||||
if (tok === n) { cnt++; continue; }
|
||||
if (n.length < 4) continue;
|
||||
const k = Math.max(n.length - 2, 4);
|
||||
if (tok.length < k || n.length < k) continue;
|
||||
if (tok.slice(0, k) === n.slice(0, k)) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
function scoreZones(text){
|
||||
const tokenStr = normalize(text).join(" ");
|
||||
const tokens = normalize(text);
|
||||
return state.directory.zones.map(z => {
|
||||
let score=0; const hits=[];
|
||||
for (const k of z.keywords){
|
||||
const n = normalize(k).join(" ");
|
||||
const c = (tokenStr.match(new RegExp("\\b"+escapeReg(n)+"\\b","g"))||[]).length;
|
||||
if (c>0){ score += c*1.0; hits.push(k); }
|
||||
let score = 0; const hits = [];
|
||||
for (const k of z.keywords) {
|
||||
const c = matchTerm(tokens, k);
|
||||
if (c > 0) { score += c * 1.0; hits.push(k); }
|
||||
}
|
||||
for (const sys of z.systems){
|
||||
const n = normalize(sys).join(" ");
|
||||
const c = (tokenStr.match(new RegExp("\\b"+escapeReg(n)+"\\b","g"))||[]).length;
|
||||
if (c>0){ score += c*1.5; hits.push(sys); }
|
||||
for (const sys of z.systems) {
|
||||
const c = matchTerm(tokens, sys);
|
||||
if (c > 0) { score += c * 1.5; hits.push(sys); }
|
||||
}
|
||||
return { zoneId:z.id, score, hits };
|
||||
}).filter(x=>x.score>0).sort((a,b)=>b.score-a.score);
|
||||
}).filter(x => x.score > 0).sort((a,b) => b.score - a.score);
|
||||
}
|
||||
function assignPerson(zoneId){
|
||||
const cs = state.directory.people.filter(p => p.zoneId===zoneId);
|
||||
|
||||
34
mvp.html
34
mvp.html
@ -252,20 +252,38 @@ function applyLang() {
|
||||
function normalize(s){
|
||||
return s.toLowerCase().replace(/ё/g,"е").replace(/[^\p{L}\p{N}\s]/gu," ").split(/\s+/).filter(Boolean);
|
||||
}
|
||||
function escapeReg(s){ return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"); }
|
||||
function matchTerm(tokens, term){
|
||||
const norm = normalize(term);
|
||||
if (norm.length === 0) return 0;
|
||||
if (norm.length > 1) {
|
||||
const joined = " " + tokens.join(" ") + " ";
|
||||
const needle = " " + norm.join(" ") + " ";
|
||||
let cnt = 0, idx = 0;
|
||||
while ((idx = joined.indexOf(needle, idx)) !== -1) { cnt++; idx += needle.length - 1; }
|
||||
return cnt;
|
||||
}
|
||||
const n = norm[0];
|
||||
let cnt = 0;
|
||||
for (const tok of tokens) {
|
||||
if (tok === n) { cnt++; continue; }
|
||||
if (n.length < 4) continue;
|
||||
const k = Math.max(n.length - 2, 4);
|
||||
if (tok.length < k || n.length < k) continue;
|
||||
if (tok.slice(0, k) === n.slice(0, k)) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
function scoreZones(text){
|
||||
const tokenStr = normalize(text).join(" ");
|
||||
const tokens = normalize(text);
|
||||
return state.directory.zones.map(z => {
|
||||
let score = 0; const hits = [];
|
||||
for (const k of z.keywords) {
|
||||
const n = normalize(k).join(" ");
|
||||
const c = (tokenStr.match(new RegExp("\\b"+escapeReg(n)+"\\b","g"))||[]).length;
|
||||
if (c>0){ score += c*1.0; hits.push(k); }
|
||||
const c = matchTerm(tokens, k);
|
||||
if (c > 0) { score += c * 1.0; hits.push(k); }
|
||||
}
|
||||
for (const sys of z.systems) {
|
||||
const n = normalize(sys).join(" ");
|
||||
const c = (tokenStr.match(new RegExp("\\b"+escapeReg(n)+"\\b","g"))||[]).length;
|
||||
if (c>0){ score += c*1.5; hits.push(sys); }
|
||||
const c = matchTerm(tokens, sys);
|
||||
if (c > 0) { score += c * 1.5; hits.push(sys); }
|
||||
}
|
||||
return { zoneId:z.id, score, hits };
|
||||
}).filter(x => x.score>0).sort((a,b)=>b.score-a.score);
|
||||
|
||||
Reference in New Issue
Block a user