Switch to JaroWinkler distance and reweight package search scores (#2840)

fix(query): use JaroWinkler distance and reweight metric components

Switch from Hamming to JaroWinkler distance for package name matching,
as JaroWinkler handles variable-length strings and gives a prefix-match
bonus better suited to package search.

Reweight the score components from sim*0.50 + simDesc*0.20 + popularity*0.30
to sim*0.35 + simDesc*0.15 + popularity*0.50 to better surface popular
packages when name similarity is moderate.

Roll back the unreleased version target from 13.0.0 to 12.6.0 to align
with the next minor release after v12.5.7.

Fix scanner.Err() check in parseStdin to surface I/O errors that were
previously silently dropped.
This commit is contained in:
Jo authored and GitHub committed 2026-06-07 18:16:46 +02:00
1 parent 86e6001498
commit ac83d3fb06
7 files changed
+88 -14

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@ import (
)
var (
yayVersion = "13.0.0" // To be set by compiler.
yayVersion = "12.6.0" // To be set by compiler.
localePath = "/usr/share/locale" // To be set by compiler.
)
+1 -1
View File
@@ -52,7 +52,7 @@ func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
popularity = a.aurSortByMetric(pkg)
}
sim = sim*0.5 + simDesc*0.2 + popularity*0.3
sim = sim*0.35 + simDesc*0.15 + popularity*0.50
a.distanceCache[pkg.name] = sim
+1 -1
View File
@@ -181,7 +181,7 @@ func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor
pkgS = RemoveInvalidTargets(s.logger, pkgS, s.targetMode)
metric := &metrics.Hamming{
metric := &metrics.JaroWinkler{
CaseSensitive: false,
}
+72 -2
View File
@@ -567,13 +567,13 @@ func TestSourceQueryBuilderSortByFields(t *testing.T) {
desc: "sort-by-metric topdown",
sortBy: "",
bottomUp: false,
wantNames: []string{"yay", "yay-git", "ruby-yard"},
wantNames: []string{"yay", "ruby-yard", "yay-git"},
},
{
desc: "sort-by-metric bottomup",
sortBy: "",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay-git", "yay"},
wantNames: []string{"yay-git", "ruby-yard", "yay"},
},
}
@@ -599,6 +599,76 @@ func TestSourceQueryBuilderSortByFields(t *testing.T) {
}
}
func TestSourceQueryBuilderChromeRanking(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
ReposFn: func() []string {
return []string{"extra"}
},
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
return nil
},
LocalPackageFn: func(string) mock.IPackage {
return nil
},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Description: "The popular web browser by Google (Stable Channel)",
Name: "google-chrome",
NumVotes: 2351,
Popularity: 13.24,
PackageBase: "google-chrome",
Version: "149.0.7827.53-1",
},
{
Description: "Extract Android APKs for running in Chrome OS OR Chrome in OS X, Linux and Windows",
Name: "chromeos-apk-git",
NumVotes: 37,
Popularity: 0.00,
PackageBase: "chromeos-apk-git",
Version: "20201218.r129.f13a94d-1",
},
{
Description: "External extension updater for Chromium based browsers",
Name: "chromexup",
NumVotes: 5,
Popularity: 0.00,
PackageBase: "chromexup",
Version: "0.5.4-5",
},
{
Description: "Standalone server that implements the W3C WebDriver standard (for goog",
Name: "chromedriver",
NumVotes: 52,
Popularity: 0.50,
PackageBase: "chromedriver",
Version: "149.0.7827.54-1",
},
}, nil
},
}
w := &strings.Builder{}
queryBuilder := NewSourceQueryBuilder(mockAUR,
text.NewLogger(w, io.Discard, strings.NewReader(""), false, "test"),
"", parser.ModeAny, "", false,
false, false)
queryBuilder.Execute(context.Background(), mockDB, []string{"chrome"})
gotNames := make([]string, len(queryBuilder.results))
for i, result := range queryBuilder.results {
gotNames[i] = result.name
}
assert.Equal(t, []string{"google-chrome", "chromedriver", "chromeos-apk-git", "chromexup"}, gotNames)
}
func newYayQueryBuilderMocks() (*mock.DBExecutor, *mockaur.MockAUR) {
mockDB := &mock.DBExecutor{
ReposFn: func() []string {
+1 -1
View File
@@ -56,7 +56,7 @@ func (migration *configSortByMigration) Do(config *Configuration) bool {
}
func (migration *configSortByMigration) TargetVersion() string {
return "13.0.0"
return "12.6.0"
}
func DefaultMigrations() []configMigration {
+8 -8
View File
@@ -188,37 +188,37 @@ func TestSortByMigration(t *testing.T) {
{
desc: "to upgrade",
testConfig: &Configuration{
Version: "12.9.0",
Version: "12.5.7",
SortBy: "name",
},
newVersion: "13.0.0",
newVersion: "12.6.0",
wantSave: true,
},
{
desc: "to upgrade-git",
testConfig: &Configuration{
Version: "12.9.0.r7.g6f60892",
Version: "12.3.0.r7.g6f60892",
SortBy: "votes",
},
newVersion: "13.0.0",
newVersion: "12.6.0",
wantSave: true,
},
{
desc: "to not upgrade",
testConfig: &Configuration{
Version: "12.9.0",
Version: "12.2.0",
SortBy: "",
},
newVersion: "13.0.0",
newVersion: "12.6.0",
wantSave: false,
},
{
desc: "to not upgrade - target version",
testConfig: &Configuration{
Version: "13.0.0",
Version: "12.6.0",
SortBy: "name",
},
newVersion: "13.0.0",
newVersion: "12.6.0",
wantSave: false,
},
{
+4
View File
@@ -613,6 +613,10 @@ func (a *Arguments) parseStdin() error {
a.AddTarget(scanner.Text())
}
if err := scanner.Err(); err != nil {
return err
}
return os.Stdin.Close()
}