Skip to content

Commit 003c75b

Browse files
committed
improved ranking
1 parent d6a7363 commit 003c75b

2 files changed

Lines changed: 95 additions & 8 deletions

File tree

interview_coder/PanelView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ private var headerBar: some View { DragHost { headerContent }.zIndex(2) }
786786
Text(hit.fileURL.lastPathComponent).lineLimit(1)
787787
}
788788
Text(tail).font(.caption2).foregroundStyle(.secondary)
789-
Text(hit.indexName + String(format: " • %.3f", hit.score)).font(.caption2).foregroundStyle(.secondary)
789+
Text(hit.indexName + ((hit.score.isFinite && hit.score < 1_000_000) ? String(format: " • %.3f", hit.score) : " • exact")).font(.caption2).foregroundStyle(.secondary)
790790
}
791791
Spacer(minLength: 4)
792792
Button("Open") { NSWorkspace.shared.activateFileViewerSelecting([hit.fileURL]) }.buttonStyle(.plain)

interview_coder/VectorIndexing.swift

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,18 @@ actor VecturaIndexService {
324324
}
325325
}
326326
}
327+
// Inject exact filename matches (by name only) to ensure visibility
328+
if !qExact.isEmpty {
329+
let qLower = qExact.lowercased()
330+
for entry in sidecar.files.values {
331+
let fname = URL(fileURLWithPath: entry.path).lastPathComponent.lowercased()
332+
if fname == qLower {
333+
let u = URL(fileURLWithPath: entry.path)
334+
// Inject exact match with a strong base score (normalized)
335+
hits.append(.init(id: name + ":exact:" + (entry.id), score: 1.0, text: "", fileURL: u, indexName: name))
336+
}
337+
}
338+
}
327339
}
328340
// Dedup by fileURL to avoid repeats across indices; sort by score desc
329341
var seen = Set<String>()
@@ -333,17 +345,92 @@ actor VecturaIndexService {
333345
seen.insert(k)
334346
return true
335347
}
336-
// Rank exact filename matches first (case-insensitive), then keep score order within groups
337-
let isExact: (IndexSearchHit) -> Bool = { h in
338-
let name = h.fileURL.lastPathComponent
339-
return !qExact.isEmpty && name.compare(qExact, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame
348+
// Rank by groups with fuzzy name similarity and embedding-first fallback:
349+
// 0 exact == query; 1 fuzzy>=0.90; 2 prefix same-ext; 3 embedding-general; 4 fuzzy>=0.75; 5 prefix other-ext; 6 contains; 7 others
350+
let nameOf: (IndexSearchHit) -> String = { $0.fileURL.lastPathComponent }
351+
let lowerQ = qExact.lowercased()
352+
let hasDot = lowerQ.contains(".")
353+
let qExt = hasDot ? (lowerQ.split(separator: ".").last.map(String.init) ?? "") : ""
354+
355+
// Precompute name similarity
356+
var simMap: [String: Double] = [:]
357+
if !lowerQ.isEmpty {
358+
for h in deduped { simMap[h.id] = nameSimilarity(nameOf(h).lowercased(), lowerQ) }
359+
}
360+
361+
func groupRank(_ h: IndexSearchHit) -> Int {
362+
let n = nameOf(h)
363+
if !qExact.isEmpty && n.compare(qExact, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame { return 0 }
364+
let sim = simMap[h.id] ?? 0
365+
if sim >= 0.90 { return 1 }
366+
let nLower = n.lowercased()
367+
if !lowerQ.isEmpty && nLower.hasPrefix(lowerQ) {
368+
if hasDot && h.fileURL.pathExtension.lowercased() == qExt { return 2 }
369+
// different extension prefix match handled later at rank 5
370+
}
371+
// Embedding-general (none of the name heuristics hit yet)
372+
if sim < 0.75 && !( !lowerQ.isEmpty && nLower.contains(lowerQ) ) { return 3 }
373+
if sim >= 0.75 { return 4 }
374+
if !lowerQ.isEmpty && nLower.hasPrefix(lowerQ) { return 5 }
375+
if !lowerQ.isEmpty && nLower.contains(lowerQ) { return 6 }
376+
return 7
377+
}
378+
379+
func clamp01(_ x: Double) -> Double { min(1.0, max(0.0, x)) }
380+
381+
func isExactName(_ h: IndexSearchHit) -> Bool {
382+
guard !qExact.isEmpty else { return false }
383+
return nameOf(h).compare(qExact, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame
384+
}
385+
386+
func combinedScore(_ h: IndexSearchHit) -> Double {
387+
let base = clamp01(h.score)
388+
let sim = simMap[h.id] ?? 0.0
389+
let nLower = nameOf(h).lowercased()
390+
let sameExtBonus: Double = (hasDot && h.fileURL.pathExtension.lowercased() == qExt && nLower.hasPrefix(lowerQ)) ? 0.12 : 0.0
391+
let containsBonus: Double = (!lowerQ.isEmpty && nLower.contains(lowerQ)) ? 0.03 : 0.0
392+
let exactBonus: Double = isExactName(h) ? 0.5 : 0.0
393+
let highFuzzyBonus: Double = sim >= 0.90 ? (0.20 * (sim - 0.90) / 0.10) : 0.0
394+
let medFuzzyBonus: Double = (sim >= 0.75 && sim < 0.90) ? (0.10 * (sim - 0.75) / 0.15) : 0.0
395+
let combined = base + exactBonus + sameExtBonus + max(highFuzzyBonus, medFuzzyBonus) + containsBonus
396+
return clamp01(combined)
397+
}
398+
399+
let ranked = deduped.sorted { a, b in
400+
// Exact always wins
401+
let ea = isExactName(a), eb = isExactName(b)
402+
if ea != eb { return ea }
403+
let ca = combinedScore(a)
404+
let cb = combinedScore(b)
405+
if ca != cb { return ca > cb }
406+
// Stable tie-breaker: higher base score, then path lexicographically
407+
if a.score != b.score { return a.score > b.score }
408+
return nameOf(a) < nameOf(b)
340409
}
341-
let exact = deduped.filter(isExact)
342-
let others = deduped.filter { !isExact($0) }
343-
let ranked = exact + others
344410
return Array(ranked.prefix(limit))
345411
}
346412

413+
// Normalized name similarity in [0,1] via Levenshtein
414+
private func nameSimilarity(_ a: String, _ b: String) -> Double {
415+
if a == b { return 1.0 }
416+
if a.isEmpty || b.isEmpty { return 0.0 }
417+
let la = Array(a); let lb = Array(b)
418+
let n = la.count; let m = lb.count
419+
var prev = Array(0...m)
420+
var cur = Array(repeating: 0, count: m + 1)
421+
for i in 1...n {
422+
cur[0] = i
423+
for j in 1...m {
424+
let cost = (la[i-1] == lb[j-1]) ? 0 : 1
425+
cur[j] = min(prev[j] + 1, cur[j-1] + 1, prev[j-1] + cost)
426+
}
427+
swap(&prev, &cur)
428+
}
429+
let dist = prev[m]
430+
let maxLen = max(n, m)
431+
return max(0.0, 1.0 - Double(dist) / Double(maxLen))
432+
}
433+
347434
// MARK: - Paths
348435
private func baseDirectory() throws -> URL {
349436
let appSupport = try fm.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)

0 commit comments

Comments
 (0)