fix: improve package search relevance with exact match priority
Package search now prioritizes results in this order: 1. Exact pname match 2. Exact attr_path match 3. pname starts with query 4. attr_path starts with query 5. FTS ranking (bm25 for SQLite, ts_rank for PostgreSQL) This ensures searching for "git" returns the "git" package first, rather than packages that merely mention "git" in their description. Also update CLAUDE.md to clarify using `nix run` instead of `go build -o` for testing binaries. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -696,6 +696,12 @@ func (s *SQLiteStore) GetPackage(ctx context.Context, revisionID int64, attrPath
|
||||
|
||||
// SearchPackages searches for packages matching a query.
|
||||
func (s *SQLiteStore) SearchPackages(ctx context.Context, revisionID int64, query string, filters PackageSearchFilters) ([]*Package, error) {
|
||||
// Query includes exact match priority:
|
||||
// - Priority 0: exact pname match
|
||||
// - Priority 1: exact attr_path match
|
||||
// - Priority 2: pname starts with query
|
||||
// - Priority 3: attr_path starts with query
|
||||
// - Priority 4: FTS match (ordered by bm25 rank)
|
||||
baseQuery := `
|
||||
SELECT p.id, p.revision_id, p.attr_path, p.pname, p.version, p.description, p.long_description, p.homepage, p.license, p.platforms, p.maintainers, p.broken, p.unfree, p.insecure
|
||||
FROM packages p
|
||||
@@ -705,6 +711,8 @@ func (s *SQLiteStore) SearchPackages(ctx context.Context, revisionID int64, quer
|
||||
|
||||
// Escape the query for FTS5 by wrapping in double quotes for literal matching.
|
||||
escapedQuery := `"` + strings.ReplaceAll(query, `"`, `""`) + `"`
|
||||
// For LIKE comparisons, escape % and _ characters
|
||||
likeQuery := strings.ReplaceAll(strings.ReplaceAll(query, "%", "\\%"), "_", "\\_")
|
||||
args := []interface{}{revisionID, escapedQuery}
|
||||
|
||||
if filters.Broken != nil {
|
||||
@@ -722,7 +730,19 @@ func (s *SQLiteStore) SearchPackages(ctx context.Context, revisionID int64, quer
|
||||
args = append(args, *filters.Insecure)
|
||||
}
|
||||
|
||||
baseQuery += " ORDER BY p.attr_path"
|
||||
// Order by exact match priority, then FTS5 rank, then attr_path
|
||||
// CASE returns priority (lower = better), bm25 returns negative scores (lower = better)
|
||||
baseQuery += ` ORDER BY
|
||||
CASE
|
||||
WHEN p.pname = ? THEN 0
|
||||
WHEN p.attr_path = ? THEN 1
|
||||
WHEN p.pname LIKE ? ESCAPE '\' THEN 2
|
||||
WHEN p.attr_path LIKE ? ESCAPE '\' THEN 3
|
||||
ELSE 4
|
||||
END,
|
||||
bm25(packages_fts),
|
||||
p.attr_path`
|
||||
args = append(args, query, query, likeQuery+"%", likeQuery+"%")
|
||||
|
||||
if filters.Limit > 0 {
|
||||
baseQuery += fmt.Sprintf(" LIMIT %d", filters.Limit)
|
||||
|
||||
Reference in New Issue
Block a user