package helpers import ( "sort" "strings" "github.com/deluan/sanitize" "github.com/dweymouth/supersonic/backend/mediaprovider" ) // name and terms should be pre-converted to the same case func AllTermsMatch(name string, terms []string) bool { for _, t := range terms { if !strings.Contains(name, t) { return false } } return true } func RankSearchResults(results []*mediaprovider.SearchResult, fullQuery string, queryTerms []string) { if len(queryTerms) == 0 || len(results) < 2 { return } sanitizeMemo := make(map[string]string, len(results)) sanitized := func(s string) string { if x, ok := sanitizeMemo[s]; ok { return x } x := strings.ToLower(sanitize.Accents(s)) sanitizeMemo[s] = x return x } sort.Slice(results, func(i, j int) bool { a, b := results[i], results[j] aName := sanitized(a.Name) bName := sanitized(b.Name) // Compare by entire query matchesA, matchesB := strings.Contains(aName, fullQuery), strings.Contains(bName, fullQuery) if matchesA && !matchesB { return true // item A has a direct match with the full query and B does not } else if matchesB && !matchesA { return false // item B matches but not A } // Compare by search query terms for _, term := range queryTerms { firstTermIdxA, firstTermIdxB := strings.Index(aName, term), strings.Index(bName, term) if firstTermIdxA >= 0 && firstTermIdxB < 0 { return true // item A has a direct match with the query term and B does not } else if firstTermIdxB >= 0 && firstTermIdxA < 0 { return false // item B matches but not A } if firstTermIdxA < firstTermIdxB { return true // item A matches the query term starting at an earlier position } else if firstTermIdxB < firstTermIdxA { return false // item B matches first } } // Defer to item type for priority order return a.Type < b.Type }) }