package api import ( "strings" "github.com/sahilm/fuzzy" ) // SearchableItems implements the fuzzy.Source interface for item search. type SearchableItems struct { items []ItemResponse fields []string } // String returns the searchable text for item at index i. func (s SearchableItems) String(i int) string { item := s.items[i] var parts []string for _, field := range s.fields { switch field { case "part_number": parts = append(parts, item.PartNumber) case "description": parts = append(parts, item.Description) case "item_type": parts = append(parts, item.ItemType) } } if len(parts) == 0 { // Default: search all text fields parts = append(parts, item.PartNumber, item.Description, item.ItemType) } return strings.Join(parts, " ") } // Len returns the number of items. func (s SearchableItems) Len() int { return len(s.items) } // FuzzyResult wraps an ItemResponse with match score. type FuzzyResult struct { ItemResponse Score int `json:"score"` } // FuzzySearch runs fuzzy matching on items and returns ranked results. func FuzzySearch(pattern string, items []ItemResponse, fields []string, limit int) []FuzzyResult { if pattern == "" || len(items) == 0 { return nil } source := SearchableItems{items: items, fields: fields} matches := fuzzy.FindFrom(pattern, source) results := make([]FuzzyResult, 0, len(matches)) for _, m := range matches { results = append(results, FuzzyResult{ ItemResponse: items[m.Index], Score: m.Score, }) } if limit > 0 && len(results) > limit { results = results[:limit] } return results }