internal/postgres: change searcher signature

The searcher type is changed from the type:

func(db *DB, ctx context.Context, q string, limit, offset, maxResultCount int) searchResponse

to:

func(db *DB, ctx context.Context, q string, limit int, opts SearchOptions) searchResponse

More fields will be added to SearchOptions in a later CL.

For golang/go#44142

Change-Id: I96e8b81e5fe10fcf3637d420eaf723c061699225
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/347609
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/postgres/search.go b/internal/postgres/search.go
index 6cbf43e..a549304 100644
--- a/internal/postgres/search.go
+++ b/internal/postgres/search.go
@@ -84,7 +84,7 @@
 }
 
 // A searcher is used to execute a single search request.
-type searcher func(db *DB, ctx context.Context, q string, limit, offset, maxResultCount int) searchResponse
+type searcher func(db *DB, ctx context.Context, q string, limit int, opts SearchOptions) searchResponse
 
 // The pkgSearchers used by Search.
 var pkgSearchers = map[string]searcher{
@@ -96,13 +96,17 @@
 	"symbol": (*DB).symbolSearch,
 }
 
+// SearchOptions provide information used by db.Search.
 type SearchOptions struct {
 	// Maximum number of results to return (page size).
 	MaxResults int
+
 	// Offset for DB query.
 	Offset int
+
 	// Maximum number to use for total result count.
 	MaxResultCount int
+
 	// If true, perform a symbol search.
 	SearchSymbols bool
 }
@@ -213,7 +217,7 @@
 	} else {
 		searchers = pkgSearchers
 	}
-	resp, err := db.hedgedSearch(ctx, q, limit, opts.Offset, opts.MaxResultCount, searchers, nil)
+	resp, err := db.hedgedSearch(ctx, q, limit, opts, searchers, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -270,8 +274,8 @@
 // available result.
 // The optional guardTestResult func may be used to allow tests to control the
 // order in which search results are returned.
-func (db *DB) hedgedSearch(ctx context.Context, q string, limit, offset, maxResultCount int, searchers map[string]searcher, guardTestResult func(string) func()) (_ *searchResponse, err error) {
-	defer derrors.WrapStack(&err, "hedgedSearch(ctx, %q, %d, %d, %d)", q, limit, offset, maxResultCount)
+func (db *DB) hedgedSearch(ctx context.Context, q string, limit int, opts SearchOptions, searchers map[string]searcher, guardTestResult func(string) func()) (_ *searchResponse, err error) {
+	defer derrors.WrapStack(&err, "hedgedSearch(ctx, %q, %d, %+v)", q, limit, opts)
 
 	searchStart := time.Now()
 	responses := make(chan searchResponse, len(searchers))
@@ -285,7 +289,7 @@
 		s := s
 		go func() {
 			start := time.Now()
-			resp := s(db, searchCtx, q, limit, offset, maxResultCount)
+			resp := s(db, searchCtx, q, limit, opts)
 			log.Debug(ctx, searchEvent{
 				Type:    resp.source,
 				Latency: time.Since(start),
@@ -329,7 +333,7 @@
 
 // deepSearch searches all packages for the query. It is slower, but results
 // are always valid.
-func (db *DB) deepSearch(ctx context.Context, q string, limit, offset, maxResultCount int) searchResponse {
+func (db *DB) deepSearch(ctx context.Context, q string, limit int, opts SearchOptions) searchResponse {
 	query := fmt.Sprintf(`
 		SELECT *, COUNT(*) OVER() AS total
 		FROM (
@@ -378,7 +382,7 @@
 			return nil
 		}
 		const fetchSize = 20 // number of rows to fetch at a time
-		err = db.db.RunQueryIncrementally(ctx, query, fetchSize, collect, q, limit, offset)
+		err = db.db.RunQueryIncrementally(ctx, query, fetchSize, collect, q, limit, opts.Offset)
 	} else {
 		collect := func(rows *sql.Rows) error {
 			var r SearchResult
@@ -389,17 +393,17 @@
 			results = append(results, &r)
 			return nil
 		}
-		err = db.db.RunQuery(ctx, query, collect, q, limit, offset)
+		err = db.db.RunQuery(ctx, query, collect, q, limit, opts.Offset)
 	}
 	if err != nil {
 		results = nil
 	}
 	for i, r := range results {
-		r.Offset = offset + i
+		r.Offset = opts.Offset + i
 	}
-	if len(results) > 0 && results[0].NumResults > uint64(maxResultCount) {
+	if len(results) > 0 && results[0].NumResults > uint64(opts.MaxResultCount) {
 		for _, r := range results {
-			r.NumResults = uint64(maxResultCount)
+			r.NumResults = uint64(opts.MaxResultCount)
 		}
 	}
 	return searchResponse{
@@ -409,7 +413,7 @@
 	}
 }
 
-func (db *DB) popularSearch(ctx context.Context, searchQuery string, limit, offset, maxResultCount int) searchResponse {
+func (db *DB) popularSearch(ctx context.Context, searchQuery string, limit int, opts SearchOptions) searchResponse {
 	query := `
 		SELECT
 			package_path,
@@ -429,20 +433,20 @@
 		results = append(results, &r)
 		return nil
 	}
-	err := db.db.RunQuery(ctx, query, collect, searchQuery, limit, offset, nonRedistributablePenalty, noGoModPenalty)
+	err := db.db.RunQuery(ctx, query, collect, searchQuery, limit, opts.Offset, nonRedistributablePenalty, noGoModPenalty)
 	if err != nil {
 		results = nil
 	}
-	numResults := maxResultCount
-	if offset+limit > maxResultCount || len(results) < limit {
+	numResults := opts.MaxResultCount
+	if opts.Offset+limit > opts.MaxResultCount || len(results) < limit {
 		// It is practically impossible that len(results) < limit, because popular
 		// search will never linearly scan everything before deep search completes,
 		// but just to be slightly more theoretically correct, if our search
 		// results are partial we know that we have exhausted all results.
-		numResults = offset + len(results)
+		numResults = opts.Offset + len(results)
 	}
 	for i, r := range results {
-		r.Offset = offset + i
+		r.Offset = opts.Offset + i
 		r.NumResults = uint64(numResults)
 	}
 	return searchResponse{
diff --git a/internal/postgres/search_test.go b/internal/postgres/search_test.go
index e942b14..b229ba6 100644
--- a/internal/postgres/search_test.go
+++ b/internal/postgres/search_test.go
@@ -455,7 +455,11 @@
 				t.Fatal(err)
 			}
 			guardTestResult := resultGuard(t, test.resultOrder)
-			resp, err := testDB.hedgedSearch(ctx, "foo", 2, 0, 100, pkgSearchers, guardTestResult)
+			opts := SearchOptions{
+				Offset:         0,
+				MaxResultCount: 100,
+			}
+			resp, err := testDB.hedgedSearch(ctx, "foo", 2, opts, pkgSearchers, guardTestResult)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -492,7 +496,7 @@
 		for name, search := range pkgSearchers {
 			if name == searcherName {
 				name := name
-				newSearchers[name] = func(*DB, context.Context, string, int, int, int) searchResponse {
+				newSearchers[name] = func(*DB, context.Context, string, int, SearchOptions) searchResponse {
 					return searchResponse{
 						source: name,
 						err:    errors.New("bad"),
@@ -543,7 +547,11 @@
 				t.Fatal(err)
 			}
 			guardTestResult := resultGuard(t, test.resultOrder)
-			resp, err := testDB.hedgedSearch(ctx, "foo", 2, 0, 100, test.searchers, guardTestResult)
+			opts := SearchOptions{
+				Offset:         0,
+				MaxResultCount: 100,
+			}
+			resp, err := testDB.hedgedSearch(ctx, "foo", 2, opts, test.searchers, guardTestResult)
 			if (err != nil) != test.wantErr {
 				t.Fatalf("hedgedSearch(): got error %v, want error: %t", err, test.wantErr)
 			}
@@ -714,7 +722,11 @@
 					test.limit = 10
 				}
 
-				got := searcher(testDB, ctx, test.searchQuery, test.limit, test.offset, 100)
+				opts := SearchOptions{
+					Offset:         test.offset,
+					MaxResultCount: 100,
+				}
+				got := searcher(testDB, ctx, test.searchQuery, test.limit, opts)
 				if got.err != nil {
 					t.Fatal(got.err)
 				}
@@ -768,7 +780,11 @@
 
 	for method, searcher := range pkgSearchers {
 		t.Run(method, func(t *testing.T) {
-			res := searcher(testDB, ctx, "foo", 10, 0, 100)
+			opts := SearchOptions{
+				Offset:         0,
+				MaxResultCount: 100,
+			}
+			res := searcher(testDB, ctx, "foo", 10, opts)
 			if res.err != nil {
 				t.Fatal(res.err)
 			}
diff --git a/internal/postgres/symbolsearch.go b/internal/postgres/symbolsearch.go
index 9ae9800..826ac31 100644
--- a/internal/postgres/symbolsearch.go
+++ b/internal/postgres/symbolsearch.go
@@ -96,7 +96,7 @@
 //
 // TODO(https://golang.org/issue/44142): factor out common code between
 // symbolSearch and deepSearch.
-func (db *DB) symbolSearch(ctx context.Context, q string, limit, offset, maxResultCount int) searchResponse {
+func (db *DB) symbolSearch(ctx context.Context, q string, limit int, opts SearchOptions) searchResponse {
 	defer middleware.ElapsedStat(ctx, "symbolSearch")()
 
 	var (
diff --git a/internal/postgres/symbolsearch_test.go b/internal/postgres/symbolsearch_test.go
index 2b9021f..5c908b4 100644
--- a/internal/postgres/symbolsearch_test.go
+++ b/internal/postgres/symbolsearch_test.go
@@ -105,7 +105,11 @@
 		},
 	} {
 		t.Run(strings.ReplaceAll(strings.ReplaceAll(test.name, "<", "_"), ">", "_"), func(t *testing.T) {
-			resp, err := testDB.hedgedSearch(ctx, test.q, 2, 0, 100, symbolSearchers, nil)
+			opts := SearchOptions{
+				Offset:         0,
+				MaxResultCount: 100,
+			}
+			resp, err := testDB.hedgedSearch(ctx, test.q, 2, opts, symbolSearchers, nil)
 			if err != nil {
 				t.Fatal(err)
 			}