about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md26
-rw-r--r--frontend/assets.go2
-rw-r--r--internal/components/data.go10
-rw-r--r--internal/components/results.templ7
-rw-r--r--internal/config/config.go2
-rw-r--r--internal/index/search.go9
-rw-r--r--internal/pagination/pagination.go31
-rw-r--r--internal/server/mux.go51
-rw-r--r--nix/package.nix2
9 files changed, 104 insertions, 36 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59f2241..759050a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,33 @@
 # Changelog
 
-## [v0.1.13](https://git.alanpearce.eu/searchix/diff/?id=v0.1.12&id2=e1787bf0b6dbac3a1601559d2f00ebc4b2df0f29) (2024-12-30)
+## [v0.1.15](https://git.alanpearce.eu/searchix/diff/?id=v0.1.14&id2=920764211064521930441b96ed914034302a7470) (2025-01-05)
 
 ### Features
 
+-   offer to show all results on one page (if < 10000)
+    ([9207642](https://git.alanpearce.eu/searchix/commit/?id=920764211064521930441b96ed914034302a7470))
+
+### Fixes
+
+-   **dev:** overwrite previous asset hash on change
+    ([d289e54](https://git.alanpearce.eu/searchix/commit/?id=d289e54af28de6b21cbe49cebc262c4e3ca9f6d2))
+
+### [v0.1.14](https://git.alanpearce.eu/searchix/diff/?id=v0.1.13&id2=v0.1.14) (2025-01-02)
+
+#### Features
+
+-   show scores in results table in dev mode
+    ([58643f3](https://git.alanpearce.eu/searchix/commit/?id=58643f38bdd62dc538a03865b1f81eb77d957779))
+
+#### Fixes
+
+-   boost non-stemmed matches over stemmed matches
+    ([797c6e2](https://git.alanpearce.eu/searchix/commit/?id=797c6e27e18f6c47ceb4266d747a05064365c32d))
+
+### [v0.1.13](https://git.alanpearce.eu/searchix/diff/?id=v0.1.12&id2=v0.1.13) (2024-12-30)
+
+#### Features
+
 -   increase results per page
     ([e1787bf](https://git.alanpearce.eu/searchix/commit/?id=e1787bf0b6dbac3a1601559d2f00ebc4b2df0f29))
 -   de-rank packages on 'all' search
diff --git a/frontend/assets.go b/frontend/assets.go
index 7a90d80..b1c2efa 100644
--- a/frontend/assets.go
+++ b/frontend/assets.go
@@ -86,10 +86,12 @@ func hashStyles() error {
 }
 
 func Rehash() (err error) {
+	Assets.Scripts = []*Asset{}
 	err = hashScripts()
 	if err != nil {
 		return err
 	}
+	Assets.Stylesheets = []*Asset{}
 	err = hashStyles()
 	if err != nil {
 		return err
diff --git a/internal/components/data.go b/internal/components/data.go
index 3625017..862429e 100644
--- a/internal/components/data.go
+++ b/internal/components/data.go
@@ -21,9 +21,9 @@ type TemplateData struct {
 
 type ResultData struct {
 	TemplateData
-	Query          string
-	ResultsPerPage int
-	Results        *search.Result
-	Prev           string
-	Next           string
+	Query   string
+	Results *search.Result
+	Prev    string
+	Next    string
+	All     string
 }
diff --git a/internal/components/results.templ b/internal/components/results.templ
index 226b71e..fee211c 100644
--- a/internal/components/results.templ
+++ b/internal/components/results.templ
@@ -1,9 +1,9 @@
 package components
 
 import (
-	"strconv"
-	"go.alanpearce.eu/searchix/internal/nix"
 	"go.alanpearce.eu/searchix/internal/config"
+	"go.alanpearce.eu/searchix/internal/nix"
+	"strconv"
 )
 
 func convertMatch[I nix.Importable](m nix.Importable) *I {
@@ -37,6 +37,9 @@ templ Results(r ResultData) {
 					}
 				</nav>
 				<span role="status">{ strconv.FormatUint(r.Results.Total, 10) } results</span>
+				if r.Next != r.Prev && r.Results.Total < config.MaxResultsShowAll {
+					<a href={ templ.SafeURL(r.All) }>Show All</a>
+				}
 			</footer>
 		} else {
 			<span role="status">Nothing found</span>
diff --git a/internal/config/config.go b/internal/config/config.go
index 33ce1da..b2af53c 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -14,6 +14,8 @@ import (
 var Version string
 var DevMode bool
 
+const MaxResultsShowAll = 10_000
+
 type URL struct {
 	*url.URL
 }
diff --git a/internal/index/search.go b/internal/index/search.go
index d576d5a..9d77488 100644
--- a/internal/index/search.go
+++ b/internal/index/search.go
@@ -15,7 +15,7 @@ import (
 	"github.com/pkg/errors"
 )
 
-const ResultsPerPage = 100
+const DefaultPageSize = 100
 
 type DocumentMatch struct {
 	*search.DocumentMatch
@@ -134,7 +134,8 @@ func (index *ReadIndex) Search(
 	ctx context.Context,
 	source *config.Source,
 	keyword string,
-	from uint64,
+	from int,
+	pageSize int,
 ) (*Result, error) {
 	query := bleve.NewBooleanQuery()
 
@@ -175,10 +176,10 @@ func (index *ReadIndex) Search(
 
 	search := bleve.NewSearchRequest(query)
 	search.Explain = config.DevMode
-	search.Size = ResultsPerPage
+	search.Size = pageSize
 
 	if from != 0 {
-		search.From = int(from)
+		search.From = from
 	}
 
 	return index.search(ctx, search)
diff --git a/internal/pagination/pagination.go b/internal/pagination/pagination.go
new file mode 100644
index 0000000..4d41587
--- /dev/null
+++ b/internal/pagination/pagination.go
@@ -0,0 +1,31 @@
+package pagination
+
+type Pagination struct {
+	total uint64
+
+	From int
+
+	Size,
+	Current,
+	Prev,
+	Next int
+
+	Needed bool
+}
+
+func New(page int, pageSize int) *Pagination {
+	return &Pagination{
+		Current: page,
+		From:    (page - 1) * pageSize,
+		Size:    pageSize,
+	}
+}
+
+func (p *Pagination) SetResults(total uint64) {
+	p.total = total
+	p.Needed = p.total > uint64(p.Size)
+	if uint64(p.Current*p.Size) <= p.total {
+		p.Next = p.Current + 1
+		p.Prev = p.Current - 1
+	}
+}
diff --git a/internal/server/mux.go b/internal/server/mux.go
index 66da7b5..83fde10 100644
--- a/internal/server/mux.go
+++ b/internal/server/mux.go
@@ -16,6 +16,7 @@ import (
 	"go.alanpearce.eu/searchix/internal/config"
 	search "go.alanpearce.eu/searchix/internal/index"
 	"go.alanpearce.eu/searchix/internal/opensearch"
+	"go.alanpearce.eu/searchix/internal/pagination"
 	"go.alanpearce.eu/x/log"
 
 	sentryhttp "github.com/getsentry/sentry-go/http"
@@ -104,17 +105,23 @@ func NewMux(
 
 			if r.URL.Query().Has("query") {
 				qs := r.URL.Query().Get("query")
-				pg := r.URL.Query().Get("page")
-				var page uint64 = 1
-				if pg != "" {
-					page, err = strconv.ParseUint(pg, 10, 64)
-					if err != nil || page == 0 {
+
+				var pageSize int = search.DefaultPageSize
+				var pageNumber = 1
+				if pg := r.URL.Query().Get("page"); pg != "" {
+					pageNumber, err = strconv.Atoi(pg)
+					if err != nil || pageNumber > math.MaxInt {
 						errorHandler(w, r, "Bad query string", http.StatusBadRequest)
 
 						return
 					}
+					if pageNumber == 0 {
+						pageNumber = 1
+						pageSize = math.MaxInt
+					}
 				}
-				results, err := index.Search(ctx, source, qs, (page-1)*search.ResultsPerPage)
+				page := pagination.New(pageNumber, pageSize)
+				results, err := index.Search(ctx, source, qs, page.From, page.Size)
 				if err != nil {
 					if err == context.DeadlineExceeded {
 						errorHandler(w, r, "Search timed out", http.StatusInternalServerError)
@@ -126,6 +133,9 @@ func NewMux(
 
 					return
 				}
+				if pageSize == math.MaxInt && results.Total > config.MaxResultsShowAll {
+					errorHandler(w, r, "Too many results, use pagination", http.StatusBadRequest)
+				}
 
 				tdata := components.ResultData{
 					TemplateData: components.TemplateData{
@@ -135,13 +145,12 @@ func NewMux(
 						Assets:        frontend.Assets,
 						Query:         qs,
 					},
-					ResultsPerPage: search.ResultsPerPage,
-					Query:          qs,
-					Results:        results,
+					Query:   qs,
+					Results: results,
 				}
 
-				hits := uint64(len(results.Hits))
-				if results.Total > hits {
+				page.SetResults(results.Total)
+				if page.Needed {
 					q, err := url.ParseQuery(r.URL.RawQuery)
 					if err != nil {
 						errorHandler(w, r, "Query string error", http.StatusBadRequest)
@@ -149,26 +158,22 @@ func NewMux(
 						return
 					}
 
-					if page > uint64(math.Ceil(float64(results.Total)/search.ResultsPerPage)) {
-						errorHandler(w, r, "Not found", http.StatusNotFound)
-
-						return
-					}
-
-					if page*search.ResultsPerPage < results.Total {
-						q.Set("page", strconv.FormatUint(page+1, 10))
+					if page.Next != 0 {
+						q.Set("page", strconv.Itoa(page.Next))
 						tdata.Next = "search?" + q.Encode()
 					}
 
-					if page > 1 {
-						p := page - 1
-						if p == 1 {
+					if page.Prev != 0 {
+						if page.Prev == 1 {
 							q.Del("page")
 						} else {
-							q.Set("page", strconv.FormatUint(p, 10))
+							q.Set("page", strconv.Itoa(page.Prev))
 						}
 						tdata.Prev = "search?" + q.Encode()
 					}
+
+					q.Set("page", "0")
+					tdata.All = "search?" + q.Encode()
 				}
 
 				w.Header().Add("Cache-Control", "max-age=300")
diff --git a/nix/package.nix b/nix/package.nix
index 9a9e2a6..61e3a84 100644
--- a/nix/package.nix
+++ b/nix/package.nix
@@ -13,7 +13,7 @@
 , css
 }:
 let
-  version = "0.1.13";
+  version = "0.1.15";
 in
 buildGoApplication {
   pname = "searchix";