Motivation With separatesources enabled (the default), a package with the exact searched name that exists in several sync repos can be listed in the wrong order. For example, if [cachyos-extra-v3] comes before [extra] in pacman.conf, extra/chromium can still show up as the top pick instead of cachyos-extra-v3/chromium. pacman prefers the repo listed first. separateSourceScore returned a flat 50 for every exact match, whatever its source. As a result, all exact matches got the same metric (50 + 1.0). The unstable slices.SortFunc then left their relative order unspecified, so the ordering added for non-exact results did not apply to exact ones. Approach Move the repo-order scoring into a repoOrderScore helper (same logic and cache as before). For exact matches, add repoOrderScore/separateSourceMax to the 50: a bonus between 0 and 1. Exact matches still outrank every non-exact result (which scores at most 45 + a metric below 1). Among exact matches, repos earlier in pacman.conf now come first. Exact AUR matches keep 50, so an exact sync-repo match now ranks strictly above an exact AUR match instead of tying with it. Scoring for non-exact results is unchanged, and nothing changes when separatesources is disabled. Validation - go test -race ./pkg/query/... -count=1: passes. - New tests TestSourceQueryBuilderExactMatchSortsByRepoOrder (top-down and bottom-up) and TestSeparateSourceScore_ExactMatchKeepsRepoOrder fail with the previous metric.go and pass with this change. - golangci-lint run ./pkg/query/...: 0 issues; gofmt clean. - go test ./... on macOS has failures that also occur on the unchanged base: Linux-only syscall fields in pkg/settings/exe, and a timezone-dependent test in pkg/text. Not tested against a real pacman setup with custom repos. Report: https://github.com/Jguer/yay/issues/2986 Assisted-by: claude-opus-5-5 (via Claude Code) Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
package query
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/adrg/strutil"
|
|
)
|
|
|
|
const minVotes = 30
|
|
const minPopularity = 0.5
|
|
const (
|
|
separateSourceMax = 45.0
|
|
separateSourceMin = 5.0
|
|
)
|
|
|
|
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
|
|
votesScore := 1 - (minVotes / (minVotes + float64(pkg.votes)))
|
|
if pkg.popularity <= 0 {
|
|
return votesScore
|
|
}
|
|
|
|
popularityScore := 1 - (minPopularity / (minPopularity + pkg.popularity))
|
|
|
|
return (votesScore + popularityScore) / 2
|
|
}
|
|
|
|
func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
|
|
name := strings.ToLower(pkg.name)
|
|
if name == a.search {
|
|
return 1.0
|
|
}
|
|
|
|
sim := strutil.Similarity(name, a.search, a.metric)
|
|
|
|
for _, prov := range pkg.provides {
|
|
// If the package provides search, it's a perfect match
|
|
// AUR packages don't populate provides
|
|
candidate := strutil.Similarity(strings.ToLower(prov), a.search, a.metric) * 0.80
|
|
if candidate > sim {
|
|
sim = candidate
|
|
}
|
|
}
|
|
|
|
simDesc := strutil.Similarity(strings.ToLower(pkg.description), a.search, a.metric)
|
|
|
|
// slightly overweight sync sources by always giving them max popularity
|
|
popularity := 1.0
|
|
if pkg.source == "aur" {
|
|
popularity = a.aurSortByMetric(pkg)
|
|
}
|
|
|
|
return sim*0.35 + simDesc*0.15 + popularity*0.50
|
|
}
|
|
|
|
func (a *abstractResults) separateSourceScore(source string, score float64) float64 {
|
|
if !a.separateSources {
|
|
return 0
|
|
}
|
|
|
|
sourceScore := a.repoOrderScore(source)
|
|
|
|
// Exact matches always rank above everything else; among them, keep
|
|
// pacman.conf repo order by adding a bonus in [0, 1].
|
|
if score == 1.0 {
|
|
return 50 + sourceScore/separateSourceMax
|
|
}
|
|
|
|
return sourceScore
|
|
}
|
|
|
|
// repoOrderScore scores sync repositories based on pacman.conf order (as reflected by dbExecutor.Repos()).
|
|
// First repo gets max, last repo gets min, evenly distributed across the range.
|
|
// AUR and unknown sources get 0.
|
|
func (a *abstractResults) repoOrderScore(source string) float64 {
|
|
if v, ok := a.separateSourceCache[source]; ok {
|
|
return v
|
|
}
|
|
|
|
// AUR is always lowest priority
|
|
if source == "aur" {
|
|
return 0
|
|
}
|
|
|
|
for i, repo := range a.repoOrder {
|
|
if repo != source {
|
|
continue
|
|
}
|
|
|
|
n := len(a.repoOrder)
|
|
if n == 1 {
|
|
a.separateSourceCache[source] = separateSourceMax
|
|
return separateSourceMax
|
|
}
|
|
|
|
step := (separateSourceMax - separateSourceMin) / float64(n-1)
|
|
sourceScore := separateSourceMax - (float64(i) * step)
|
|
a.separateSourceCache[source] = sourceScore
|
|
return sourceScore
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (a *abstractResults) calculateMetric(pkg *abstractResult) float64 {
|
|
score := a.GetMetric(pkg)
|
|
return a.separateSourceScore(pkg.source, score) + score
|
|
}
|