internal/postgres: in search, group stdlib packages by top-level dir
When grouping search results, group packages from the standard library
by their top-level directory, instead of treating them all as part of a single module.
So "net", "net/http" and "net/url" will get grouped together.
Change-Id: Ib638c39de56ecde104d569a607255d2b6a74ce55
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/346969
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/postgres/search.go b/internal/postgres/search.go
index 670792c..359c113 100644
--- a/internal/postgres/search.go
+++ b/internal/postgres/search.go
@@ -9,6 +9,7 @@
"database/sql"
"fmt"
"io"
+ "path"
"sort"
"strings"
"time"
@@ -540,29 +541,29 @@
//
// Higher tagged major versions of a module replace lower ones.
func groupSearchResults(rs []*SearchResult) []*SearchResult {
- bestInSeries := map[string]*SearchResult{} // series path to result with max major version
+ bestInGroup := map[string]*SearchResult{} // series path to result with max major version
// Since rs is sorted by score, the first package we see for a series is the
// highest-ranked one. However, we may prefer to show a lower-ranked one from a
// module in the same series with a higher major version.
for _, r := range rs {
- seriesPath, rMajor := internal.SeriesPathAndMajorVersion(r.ModulePath)
- b := bestInSeries[seriesPath]
+ group, rMajor := groupAndMajorVersion(r)
+ b := bestInGroup[group]
if b == nil {
- // First result (package) with this series path; remember it.
- bestInSeries[seriesPath] = r
+ // First result (package) with this key; remember it.
+ bestInGroup[group] = r
r.OtherMajor = map[string]bool{}
} else {
- _, bMajor := internal.SeriesPathAndMajorVersion(b.ModulePath)
+ _, bMajor := groupAndMajorVersion(b)
switch {
case !version.IsPseudo(r.Version) && (rMajor > bMajor || version.IsPseudo(b.Version)):
// r is tagged, and is either in a higher major version, or the current best
// is not tagged. Either way, prefer r to b.
- bestInSeries[seriesPath] = r
+ bestInGroup[group] = r
r.OtherMajor = b.OtherMajor
r.OtherMajor[b.ModulePath] = true
r.Score = b.Score // inherit the lower major version's higher score
case rMajor == bMajor:
- // r is another package from the module of b; remember it there.
+ // r is another package in b's group; remember it there.
b.SameModule = append(b.SameModule, r)
default:
@@ -574,7 +575,7 @@
}
// Collect new results and re-sort by score.
var results []*SearchResult
- for _, r := range bestInSeries {
+ for _, r := range bestInGroup {
if len(r.OtherMajor) == 0 {
r.OtherMajor = nil
}
@@ -586,6 +587,19 @@
return results
}
+func groupAndMajorVersion(r *SearchResult) (string, int) {
+ // Packages in the standard library are grouped by their top-level
+ // directory, and we can consider them all part of the same major version.
+ if r.ModulePath == stdlib.ModulePath {
+ dir := r.PackagePath
+ if strings.ContainsRune(dir, '/') {
+ dir = path.Dir(dir)
+ }
+ return dir, 1
+ }
+ return internal.SeriesPathAndMajorVersion(r.ModulePath)
+}
+
// numRows counts the number of rows in a slice of SearchResults.
// Grouping will put some rows inside a SearchResult.
func numRows(rs []*SearchResult) int {
diff --git a/internal/postgres/search_test.go b/internal/postgres/search_test.go
index e5edca6..02a0cba 100644
--- a/internal/postgres/search_test.go
+++ b/internal/postgres/search_test.go
@@ -25,6 +25,7 @@
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/experiment"
"golang.org/x/pkgsite/internal/licenses"
+ "golang.org/x/pkgsite/internal/stdlib"
"golang.org/x/pkgsite/internal/testing/sample"
)
@@ -1438,6 +1439,25 @@
{Name: "m1", ModulePath: "m1", Version: "v0.0.0", Score: 10, OtherMajor: set("m1/v2", "m1/v3")},
},
},
+ {
+ name: "stdlib",
+ in: []*SearchResult{
+ {PackagePath: "net", ModulePath: stdlib.ModulePath, Score: 10},
+ {PackagePath: "m1", ModulePath: "m1", Version: "v0.0.0", Score: 9},
+ {PackagePath: "net/http", ModulePath: stdlib.ModulePath, Score: 8},
+ {PackagePath: "encoding/json", ModulePath: stdlib.ModulePath, Score: 7},
+ {PackagePath: "encoding/gob", ModulePath: stdlib.ModulePath, Score: 6},
+ },
+ want: []*SearchResult{
+ {PackagePath: "net", ModulePath: stdlib.ModulePath, Score: 10, SameModule: []*SearchResult{
+ {PackagePath: "net/http", ModulePath: stdlib.ModulePath, Score: 8},
+ }},
+ {PackagePath: "m1", ModulePath: "m1", Version: "v0.0.0", Score: 9},
+ {PackagePath: "encoding/json", ModulePath: stdlib.ModulePath, Score: 7, SameModule: []*SearchResult{
+ {PackagePath: "encoding/gob", ModulePath: stdlib.ModulePath, Score: 6},
+ }},
+ },
+ },
} {
t.Run(test.name, func(t *testing.T) {
got := groupSearchResults(test.in)