feat: Enhance search ranking with name matching and field extraction improvements
Adds nameMatchBonus scoring to prioritize results where node names exactly or partially match query terms. Implements dedicated field extraction for Java/C# to properly categorize class fields vs variables. Optimizes BM25 search with column weights favoring name matches and increased result fetching before post-processing. Refines stop words list to preserve common programming terms like "get", "find", "list".
This commit is contained in:
@@ -19,13 +19,13 @@ export const STOP_WORDS = new Set([
|
||||
'should', 'may', 'might', 'can', 'shall', 'not', 'no', 'all', 'each',
|
||||
'every', 'how', 'what', 'where', 'when', 'who', 'which', 'why',
|
||||
'i', 'me', 'my', 'we', 'our', 'you', 'your', 'he', 'she', 'they',
|
||||
'find', 'show', 'get', 'list', 'give', 'tell',
|
||||
'been', 'done', 'made', 'used', 'using', 'work', 'works', 'found',
|
||||
'show', 'give', 'tell',
|
||||
'been', 'done', 'made', 'used', 'using', 'works', 'found',
|
||||
'also', 'into', 'then', 'than', 'just', 'more', 'some', 'such',
|
||||
'over', 'only', 'new', 'out', 'its', 'so', 'up', 'as', 'if',
|
||||
// Code-specific noise
|
||||
'over', 'only', 'out', 'its', 'so', 'up', 'as', 'if',
|
||||
// Code-specific noise (avoid filtering common symbol names like get/set/add/build/find/list)
|
||||
'code', 'file', 'files', 'function', 'method', 'class', 'type',
|
||||
'build', 'fix', 'bug', 'called', 'set', 'add',
|
||||
'fix', 'bug', 'called',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -140,6 +140,48 @@ export function isTestFile(filePath: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bonus when a node's name matches the search query.
|
||||
* Exact matches get the largest boost; prefix matches get smaller boosts.
|
||||
* Multi-word queries also check individual term matches against the name.
|
||||
*/
|
||||
export function nameMatchBonus(nodeName: string, query: string): number {
|
||||
const nameLower = nodeName.toLowerCase();
|
||||
|
||||
// Split query into word-level terms (handles "CacheBuilder build" → ["cache","builder","build"])
|
||||
const rawTerms = query
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||
.split(/[\s_.\-]+/)
|
||||
.map(t => t.toLowerCase())
|
||||
.filter(t => t.length >= 2);
|
||||
|
||||
// Also keep original space-separated tokens for exact-term matching
|
||||
const queryTokens = query.split(/\s+/).map(t => t.toLowerCase()).filter(t => t.length >= 2);
|
||||
|
||||
// Full query as a single token (for compound identifiers like "CacheBuilder")
|
||||
const queryLower = query.replace(/[\s]+/g, '').toLowerCase();
|
||||
|
||||
// Exact match: query exactly equals the node name
|
||||
if (nameLower === queryLower) return 30;
|
||||
|
||||
// Exact match on a query token: "CacheBuilder build" and node name is "build"
|
||||
if (queryTokens.length > 1 && queryTokens.includes(nameLower)) return 25;
|
||||
|
||||
// Name starts with query (prefix search: "Cache" → "CacheBuilder")
|
||||
if (nameLower.startsWith(queryLower)) return 20;
|
||||
|
||||
// All camelCase-split terms appear in the name
|
||||
if (rawTerms.length > 1) {
|
||||
const allMatch = rawTerms.every(t => nameLower.includes(t));
|
||||
if (allMatch) return 15;
|
||||
}
|
||||
|
||||
// Name contains the full query as substring
|
||||
if (nameLower.includes(queryLower)) return 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kind-based bonus for search ranking
|
||||
* Functions and classes are typically more relevant than variables/imports
|
||||
|
||||
Reference in New Issue
Block a user