diff --git a/ashi/ashi.js b/ashi/ashi.js index a96c9ba..db6e885 100644 --- a/ashi/ashi.js +++ b/ashi/ashi.js @@ -21,40 +21,50 @@ async function searchResults(query) { const t = title.toLowerCase().trim(); if (t === q) return 1000; - if (t.includes(q)) return 900; + + if (t.startsWith(q + ' ') || t.startsWith(q + ':') || t.startsWith(q + '-')) return 950; + + const wordBoundaryRegex = new RegExp(`\\b${q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`); + if (wordBoundaryRegex.test(t)) return 900; const qTokens = q.split(/\s+/).filter(token => token.length > 0); - const tTokens = t.split(/[\s\-]+/).filter(token => token.length > 0); + const tTokens = t.split(/[\s\-:]+/).filter(token => token.length > 0); - const stopwords = new Set(['the', 'a', 'an', 'and', 'or', 'of', 'in', 'on', 'at', 'to', 'for']); + const stopwords = new Set(['the', 'a', 'an', 'and', 'or', 'of', 'in', 'on', 'at', 'to', 'for', 'with']); let score = 0; - let matchedTokens = 0; + let exactMatches = 0; + let partialMatches = 0; let significantMatches = 0; qTokens.forEach(qToken => { const isStopword = stopwords.has(qToken); let bestMatch = 0; + let hasExactMatch = false; tTokens.forEach(tToken => { let matchScore = 0; if (tToken === qToken) { - matchScore = isStopword ? 20 : 100; + matchScore = isStopword ? 25 : 120; + hasExactMatch = true; if (!isStopword) significantMatches++; - } else if (tToken.includes(qToken) && qToken.length >= 3) { - matchScore = isStopword ? 10 : 60; + } + else if (qToken.includes(tToken) && tToken.length >= 3 && qToken.length <= tToken.length + 2) { + matchScore = isStopword ? 8 : 40; if (!isStopword) significantMatches++; - } else if (qToken.includes(tToken) && tToken.length >= 3) { - matchScore = isStopword ? 10 : 50; + } + else if (tToken.startsWith(qToken) && qToken.length >= 3) { + matchScore = isStopword ? 12 : 70; if (!isStopword) significantMatches++; - } else if (qToken.length >= 3 && tToken.length >= 3) { + } + else if (qToken.length >= 4 && tToken.length >= 4) { const dist = levenshteinDistance(qToken, tToken); const maxLen = Math.max(qToken.length, tToken.length); const similarity = 1 - (dist / maxLen); - if (similarity > 0.7) { - matchScore = Math.floor(similarity * 40); + if (similarity > 0.8) { + matchScore = Math.floor(similarity * 60); if (!isStopword) significantMatches++; } } @@ -64,37 +74,39 @@ async function searchResults(query) { if (bestMatch > 0) { score += bestMatch; - matchedTokens++; + if (hasExactMatch) exactMatches++; + else partialMatches++; } }); const significantTokens = qTokens.filter(t => !stopwords.has(t)).length; - const requiredMatches = Math.ceil(significantTokens * 0.7); + const requiredMatches = Math.max(1, Math.ceil(significantTokens * 0.8)); if (significantMatches < requiredMatches) { - return 0; + return 0; } - if (matchedTokens >= qTokens.length) { - score += 100; + if (exactMatches + partialMatches >= qTokens.length) { + score += 80; } - if (t.startsWith(q)) { - score += 150; - } + score += exactMatches * 20; const extraWords = tTokens.length - qTokens.length; - if (extraWords > 3) { - score -= (extraWords - 3) * 20; + if (extraWords > 2) { + score -= (extraWords - 2) * 25; } + let orderBonus = 0; for (let i = 0; i < qTokens.length - 1; i++) { - const bigram = qTokens[i] + ' ' + qTokens[i + 1]; - const bigramNoDash = qTokens[i] + qTokens[i + 1]; - if (!t.includes(bigram) && !t.includes(bigramNoDash)) { - score -= 15; + const currentTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i])); + const nextTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i + 1])); + + if (currentTokenIndex !== -1 && nextTokenIndex !== -1 && currentTokenIndex < nextTokenIndex) { + orderBonus += 15; } } + score += orderBonus; return Math.max(0, score); }; @@ -248,9 +260,9 @@ async function searchResults(query) { })); const filteredResults = scoredResults - .filter(r => r.score > 0) - .sort((a, b) => b.score - a.score) - .map(({ score, ...rest }) => rest); + .filter(r => r.score > 50) // Increased threshold to filter out weak matches + .sort((a, b) => b.score - a.score) // Sort by pre-calculated scores + .map(({ score, ...rest }) => rest); return JSON.stringify(filteredResults.length > 0 ? filteredResults : [{ href: "",