internal/postgres: repeat search when grouping
When grouping results, search a second time with a larger limit if the
first doesn't return enough results.
Also, remove the Limit search option; it is an implementation detail
that should be determined by DB.Search.
I tested this against 176 distinct queries obtained from the logs over
a 1-hour period, with max results = 10. Although it was somewhat
slower than without grouping, the 99%ile was still under 1 second.
For golang/go#47320
Change-Id: Ie09d6b83bc270a7113abd74b03daf26fa6054b0f
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/336950
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/internal/postgres/search.go b/internal/postgres/search.go
index 411dd87..cfb5ab4 100644
--- a/internal/postgres/search.go
+++ b/internal/postgres/search.go
@@ -101,8 +101,6 @@
type SearchOptions struct {
// Maximum number of results to return (page size).
MaxResults int
- // Limit for the DB query; defaults to MaxResults.
- Limit int
// Offset for DB query.
Offset int
// Maximum number to use for total result count.
@@ -184,9 +182,30 @@
// the penalty of a deep search that scans nearly every package.
func (db *DB) Search(ctx context.Context, q string, opts SearchOptions) (_ []*SearchResult, err error) {
defer derrors.WrapStack(&err, "DB.Search(ctx, %q, %+v)", q, opts)
- if opts.Limit == 0 {
- opts.Limit = opts.MaxResults
+ if experiment.IsActive(ctx, internal.ExperimentSearchGrouping) && !opts.SearchSymbols {
+ const (
+ limitMultiplier1 = 3
+ limitMultiplier2 = 5
+ )
+ // Limit search to more rows than the requested number of results, so
+ // that it can find other packages in the modules it selects.
+ srs, err := db.search(ctx, q, opts, limitMultiplier1*opts.MaxResults)
+ if err != nil {
+ return nil, err
+ }
+ if len(srs) >= opts.MaxResults || numRows(srs) <= limitMultiplier1*opts.MaxResults {
+ return srs, nil
+ }
+ // Grouped search didn't find enough results, but there are more
+ // rows that could potentially match. Try one more time, with a
+ // larger limit.
+ return db.search(ctx, q, opts, limitMultiplier2*opts.MaxResults)
}
+ return db.search(ctx, q, opts, opts.MaxResults)
+}
+
+func (db *DB) search(ctx context.Context, q string, opts SearchOptions, limit int) (_ []*SearchResult, err error) {
+ defer derrors.WrapStack(&err, "search(limit=%d)", limit)
var searchers map[string]searcher
if opts.SearchSymbols &&
@@ -196,7 +215,7 @@
} else {
searchers = pkgSearchers
}
- resp, err := db.hedgedSearch(ctx, q, opts.Limit, opts.Offset, opts.MaxResultCount, searchers, nil)
+ resp, err := db.hedgedSearch(ctx, q, limit, opts.Offset, opts.MaxResultCount, searchers, nil)
if err != nil {
return nil, err
}
@@ -572,6 +591,16 @@
return results
}
+// numRows counts the number of rows in a slice of SearchResults.
+// Grouping will put some rows inside a SearchResult.
+func numRows(rs []*SearchResult) int {
+ n := 0
+ for _, r := range rs {
+ n += 1 + len(r.SameModule)
+ }
+ return n
+}
+
var upsertSearchStatement = fmt.Sprintf(`
INSERT INTO search_documents (
package_path,