Improve camera model search with per-model ranking and two-stage loading

- Split camera results into individual models (Brand: Model format)
- Add model-specific relevance scoring for better search results
- Implement two-stage autocomplete: 10 results immediately, 50 after 1 second
- Filter out "Other" models from search results
- Sort models by relevance score (exact matches first)
- Add auto.json brand for automatic detection fallback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
eduard256
2025-11-06 00:43:03 +03:00
parent 1cfc2fa2e5
commit 74fe12bcf1
15 changed files with 2222 additions and 16 deletions
+59 -9
View File
@@ -58,21 +58,71 @@ func (s *SearchEngine) Search(query string, limit int) (*models.CameraSearchResp
return results[i].Score > results[j].Score
})
// Apply limit
if len(results) > limit {
results = results[:limit]
// Expand each camera into individual model entries with model-specific scores
type ModelResult struct {
Camera models.Camera
Score float64
}
var modelResults []ModelResult
for _, result := range results {
camera := result.Camera
// Collect unique models with their scores
modelScores := make(map[string]float64)
for _, entry := range camera.Entries {
for _, model := range entry.Models {
if model != "" && model != "Other" {
// Calculate model-specific score
modelScore := s.calculateModelScore(model, modelTokens, normalizedQuery)
if modelScore > modelScores[model] {
modelScores[model] = modelScore
}
}
}
}
// Create a separate camera entry for each unique model
for model, modelScore := range modelScores {
// Combine brand score with model score
finalScore := result.Score*0.3 + modelScore*0.7
expandedCamera := models.Camera{
Brand: camera.Brand,
BrandID: camera.BrandID,
Model: model,
LastUpdated: camera.LastUpdated,
Source: camera.Source,
Website: camera.Website,
Entries: camera.Entries,
MatchScore: finalScore,
}
modelResults = append(modelResults, ModelResult{
Camera: expandedCamera,
Score: finalScore,
})
}
}
// Convert to response
cameras := make([]models.Camera, len(results))
for i, result := range results {
cameras[i] = *result.Camera
cameras[i].MatchScore = result.Score
// Sort by final score (best matches first)
sort.Slice(modelResults, func(i, j int) bool {
return modelResults[i].Score > modelResults[j].Score
})
// Apply limit
if len(modelResults) > limit {
modelResults = modelResults[:limit]
}
// Convert to camera slice
cameras := make([]models.Camera, len(modelResults))
for i, result := range modelResults {
cameras[i] = result.Camera
}
return &models.CameraSearchResponse{
Cameras: cameras,
Total: len(results),
Total: len(cameras),
Returned: len(cameras),
}, nil
}