internal/frontend: don't paginate for grouped search
When the search-grouping experiment is on, don't paginate the
results. Instead present all of them, and let the user choose how many
they want from a fixed list.
For golang/go#47315
Change-Id: Ia7639f619bf1cce62067c69983f946d964161b17
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/336770
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>
diff --git a/internal/frontend/paginate.go b/internal/frontend/paginate.go
index c651380..7af08ad 100644
--- a/internal/frontend/paginate.go
+++ b/internal/frontend/paginate.go
@@ -20,7 +20,7 @@
// 1, 2, 3, .... Each page except possibly the last has the same number of results.
type pagination struct {
baseURL *url.URL // URL common to all pages
- limit int // the maximum number of results on a page
+ Limit int // the maximum number of results on a page
ResultCount int // number of results on this page
TotalCount int // total number of results
Approximate bool // whether or not the total count is approximate
@@ -29,6 +29,7 @@
NextPage int // " " " next page, usually Page+1, but zero on the last page
Offset int // offset of the first item on the current page
Pages []int // consecutive page numbers to be displayed for navigation
+ Limits []int // limits to be displayed
}
// PageURL constructs a URL that displays the given page.
@@ -40,6 +41,15 @@
return p.baseURL.String()
}
+// LimitURL constructs a URL that adds a "limit" query parameter to the base
+// URL.
+func (p pagination) LimitURL(limit int) string {
+ newQuery := p.baseURL.Query()
+ newQuery.Set("limit", strconv.Itoa(limit))
+ p.baseURL.RawQuery = newQuery.Encode()
+ return p.baseURL.String()
+}
+
// newPagination constructs a pagination. Call it after some results have been
// obtained.
// resultCount is the number of results in the current page.
@@ -50,11 +60,12 @@
TotalCount: totalCount,
ResultCount: resultCount,
Offset: params.offset(),
- limit: params.limit,
+ Limit: params.limit,
Page: params.page,
PrevPage: prev(params.page),
NextPage: next(params.page, params.limit, totalCount),
Pages: pagesToLink(params.page, numPages(params.limit, totalCount), defaultNumPagesToLink),
+ Limits: []int{10, 30, maxSearchPageSize},
}
}
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index 6b7d633..3ef24bb 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -67,9 +67,14 @@
func fetchSearchPage(ctx context.Context, db *postgres.DB, query string, pageParams paginationParams, searchSymbols bool) (*SearchPage, error) {
maxResultCount := maxSearchOffset + pageParams.limit
+ offset := pageParams.offset()
+ if experiment.IsActive(ctx, internal.ExperimentSearchGrouping) {
+ // When using search grouping, do pageless search: always start from the beginning.
+ offset = 0
+ }
dbresults, err := db.Search(ctx, query, postgres.SearchOptions{
MaxResults: pageParams.limit,
- Offset: pageParams.offset(),
+ Offset: offset,
MaxResultCount: maxResultCount,
SearchSymbols: searchSymbols,
})
diff --git a/internal/frontend/search_test.go b/internal/frontend/search_test.go
index d6ef346..afdaaa3 100644
--- a/internal/frontend/search_test.go
+++ b/internal/frontend/search_test.go
@@ -111,9 +111,10 @@
ResultCount: 1,
PrevPage: 0,
NextPage: 0,
- limit: 20,
+ Limit: 20,
Page: 1,
Pages: []int{1},
+ Limits: []int{10, 30, 100},
},
Results: []*SearchResult{
{
@@ -137,9 +138,10 @@
ResultCount: 1,
PrevPage: 0,
NextPage: 0,
- limit: 20,
+ Limit: 20,
Page: 1,
Pages: []int{1},
+ Limits: []int{10, 30, 100},
},
Results: []*SearchResult{
{
diff --git a/internal/frontend/server_test.go b/internal/frontend/server_test.go
index cf68c81..1d94929 100644
--- a/internal/frontend/server_test.go
+++ b/internal/frontend/server_test.go
@@ -703,17 +703,6 @@
want: in("", hasText("User-agent: *"), hasText(regexp.QuoteMeta("Disallow: /search?*"))),
},
{
- name: "search",
- urlPath: fmt.Sprintf("/search?q=%s", sample.PackageName),
- wantStatusCode: http.StatusOK,
- want: in("",
- in(".SearchResults-resultCount", hasText("2 results")),
- in(".LegacySearchSnippet-header",
- in("a",
- href("/"+sample.ModulePath+"/foo"),
- hasText(sample.ModulePath+"/foo")))),
- },
- {
name: "search large offset",
urlPath: "/search?q=github.com&page=1002",
wantStatusCode: http.StatusBadRequest,
@@ -1129,6 +1118,48 @@
},
}
+var searchTestCase = serverTestCase{
+ name: "search",
+ urlPath: fmt.Sprintf("/search?q=%s", sample.PackageName),
+ wantStatusCode: http.StatusOK,
+ want: in("",
+ in(".SearchResults-resultCount", hasText("2 results")),
+ in(".LegacySearchSnippet-header",
+ in("a",
+ href("/"+sample.ModulePath+"/foo"),
+ hasText(sample.ModulePath+"/foo")))),
+}
+
+var searchGroupingTestCases = []serverTestCase{
+ {
+ name: "search",
+ urlPath: fmt.Sprintf("/search?q=%s", sample.PackageName),
+ wantStatusCode: http.StatusOK,
+ want: in("",
+ in(".SearchResults-resultCount", hasText("2 results")),
+ in(".SearchSnippet-header",
+ in("a",
+ href("/"+sample.ModulePath+"/foo"),
+ hasText("foo")))),
+ },
+ {
+ name: "pageless search",
+ urlPath: fmt.Sprintf("/search?q=%s", sample.PackageName),
+ wantStatusCode: http.StatusOK,
+ want: in("",
+ in(".SearchResults-resultCount",
+ hasText("2 results"),
+ hasText("Fetch"),
+ hasText("results.")),
+ notIn(".Pagination-navInner")),
+ },
+ {
+ name: "search large limit",
+ urlPath: fmt.Sprintf("/search?q=%s&limit=101", sample.PackageName),
+ wantStatusCode: http.StatusBadRequest,
+ },
+}
+
// TestServer checks the contents of served pages by looking for
// strings and elements in the parsed HTML response body.
//
@@ -1150,7 +1181,7 @@
{
name: "no experiments",
testCasesFunc: func() []serverTestCase {
- return append(serverTestCases(), linksTestCases...)
+ return append(append(serverTestCases(), linksTestCases...), searchTestCase)
},
},
{
@@ -1173,6 +1204,13 @@
internal.ExperimentSymbolHistoryVersionsPage,
},
},
+ {
+ name: "search grouping",
+ testCasesFunc: func() []serverTestCase {
+ return append(serverTestCases(), searchGroupingTestCases...)
+ },
+ experiments: []string{internal.ExperimentSearchGrouping},
+ },
} {
t.Run(test.name, func(t *testing.T) {
testServer(t, test.testCasesFunc(), test.experiments...)
@@ -1565,6 +1603,7 @@
{"homepage", nil, basePage{}},
{"license-policy", nil, licensePolicyPage{}},
{"search", nil, SearchPage{}},
+ {"legacy_search", nil, SearchPage{}},
{"search-help", nil, basePage{}},
{"unit_details.tmpl", nil, UnitPage{}},
{
diff --git a/static/frontend/legacy_search/legacy_search.tmpl b/static/frontend/legacy_search/legacy_search.tmpl
index cd3a0e9..7347763 100644
--- a/static/frontend/legacy_search/legacy_search.tmpl
+++ b/static/frontend/legacy_search/legacy_search.tmpl
@@ -75,7 +75,6 @@
{{range $i, $v := .Links}}
<a href="/{{$v.Href}}" data-gtmc="search result" data-gtmv="{{$i}}">{{$v.Body}}</a>
{{end}}
- <span>{{.Suffix}}</span>
</div>
{{end}}
{{with .OtherMajor}}
diff --git a/static/frontend/search/search.tmpl b/static/frontend/search/search.tmpl
index ed26898..672b675 100644
--- a/static/frontend/search/search.tmpl
+++ b/static/frontend/search/search.tmpl
@@ -23,7 +23,21 @@
<div class="go-Content">
<h1 class="SearchResults-header">Results for “{{.Query}}”</h1>
<div class="SearchResults-resultCount go-textSubtle">
- {{template "pagination_summary" .Pagination}} {{pluralize .Pagination.TotalCount "result"}}
+ {{with .Pagination}}
+ {{ $p := . }}
+ <div class="Pagination-nav">
+ Displaying {{.ResultCount}} of {{if .Approximate}}about {{end}}{{.TotalCount}} {{pluralize .TotalCount "result"}}.
+ Fetch
+ {{range .Limits}}
+ {{if eq . $p.Limit}}
+ <b class="Pagination-number">{{.}}</b>
+ {{else}}
+ <a class="Pagination-number" href="{{$p.LimitURL .}}">{{.}}</a>
+ {{end}}
+ {{end}}
+ results.
+ </div>
+ {{end}}
<div class="SearchResults-help"><a href="/search-help">Search help</a></div>
</div>
{{if eq (len .Results) 0}}
@@ -46,9 +60,6 @@
{{template "grouped_search" .}}
</div>
{{end}}
- <div class="SearchResults-footer">
- {{template "pagination_nav" .Pagination}}
- </div>
</div>
</main>
{{end}}
@@ -65,7 +76,7 @@
{{$v.Name}}
</a>
</h2>
- <span class="SearchSnippet-header-path">{{$v.PackagePath}}</span>
+ <span class="SearchSnippet-header-path">{{$v.PackagePath}}</span>
</div>
<p class="SearchSnippet-synopsis" data-test-id="snippet-synopsis">
{{$v.Synopsis}}