+9
-9
src/search.rs
+9
-9
src/search.rs
···
105
105
scored_results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
106
106
scored_results.truncate(query.top_k);
107
107
108
-
// Normalize scores to 0-0.95 range
109
-
let max_score = scored_results.first().map(|(_, s)| *s).unwrap_or(1.0);
110
-
let min_score = scored_results.last().map(|(_, s)| *s).unwrap_or(0.0);
111
-
let score_range = (max_score - min_score).max(0.001); // avoid division by zero
112
-
108
+
// Scale RRF scores to 0-1 range
109
+
// RRF scores typically range from ~0.016 (single match, low rank) to ~0.033 (dual match, high rank)
110
+
// Scale by 25x to map good matches near 1.0, poor matches stay low
113
111
let results: Vec<BufoResult> = scored_results
114
112
.into_iter()
115
-
.filter_map(|(id, raw_score)| {
113
+
.filter_map(|(id, rrf_score)| {
116
114
all_results.get(&id).map(|row| {
117
115
let url = row
118
116
.attributes
···
128
126
.unwrap_or(&row.id)
129
127
.to_string();
130
128
131
-
// Normalize score to 0-0.95 range
132
-
let normalized_score = ((raw_score - min_score) / score_range) * 0.95;
129
+
// Scale and clamp RRF score to 0-1 range
130
+
// Good matches (appearing high in both searches) will approach 1.0
131
+
// Weak matches will naturally be lower
132
+
let scaled_score = (rrf_score * 25.0).min(1.0);
133
133
134
134
BufoResult {
135
135
id: row.id.clone(),
136
136
url,
137
137
name,
138
-
score: normalized_score,
138
+
score: scaled_score,
139
139
}
140
140
})
141
141
})
+4
static/index.html
+4
static/index.html
···
76
76
.search-input-wrapper {
77
77
display: flex;
78
78
gap: 10px;
79
+
width: 100%;
79
80
}
80
81
81
82
input[type="text"] {
82
83
flex: 1;
84
+
min-width: 0;
83
85
padding: 15px;
84
86
border: 2px solid #e0e0e0;
85
87
border-radius: 8px;
···
102
104
font-weight: 600;
103
105
cursor: pointer;
104
106
transition: background 0.3s;
107
+
white-space: nowrap;
108
+
flex-shrink: 0;
105
109
}
106
110
107
111
button:hover {