(item: SearchIndexItem, query: string)
| 151 | } |
| 152 | |
| 153 | function scoreSearchItem(item: SearchIndexItem, query: string) { |
| 154 | const terms = normalizeSearchText(query).split(/\s+/).filter(Boolean); |
| 155 | if (terms.length === 0) return 1; |
| 156 | |
| 157 | let score = 0; |
| 158 | for (const term of terms) { |
| 159 | let best = 0; |
| 160 | for (const field of searchFields(item)) { |
| 161 | best = Math.max(best, scoreTerm(field, term)); |
| 162 | } |
| 163 | if (best <= 0) return 0; |
| 164 | score += best; |
| 165 | } |
| 166 | |
| 167 | const normalizedTitle = normalizeSearchText(item.title); |
| 168 | const normalizedId = normalizeSearchText(item.id); |
| 169 | const normalizedQuery = normalizeSearchText(query); |
| 170 | if (normalizedTitle === normalizedQuery || normalizedId === normalizedQuery) { |
| 171 | score += 120; |
| 172 | } else if (normalizedTitle.startsWith(normalizedQuery)) { |
| 173 | score += 44; |
| 174 | } else if (normalizedId.startsWith(normalizedQuery)) { |
| 175 | score += 36; |
| 176 | } |
| 177 | |
| 178 | if (item.type === "model") score += 8; |
| 179 | return score; |
| 180 | } |
| 181 | |
| 182 | function rankSearchItems(query: string) { |
| 183 | const normalizedQuery = normalizeSearchText(query); |
no test coverage detected