Jump the Review list to an artist by letter
An A–Z/# bar above the Songs and Albums lists jumps to the first artist starting with that letter. Songs come from a new /letters endpoint giving each leading character's offset; the song list now sorts case-insensitively like the album list so each letter stays together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
095e294f4b
commit
00147aaa9e
5 files changed
+160
-16
No files matched your search
@@ -432,7 +432,7 @@ func (s *Store) Matches(ctx context.Context, pairID int64, f MatchFilter) ([]Mat
|
||||
f.Limit = 100
|
||||
}
|
||||
rows, err := s.DB.QueryContext(ctx, `SELECT `+matchCols+` FROM matches WHERE `+w+
|
||||
` ORDER BY rel_slave LIMIT ? OFFSET ?`, append(args, f.Limit, f.Offset)...)
|
||||
` ORDER BY rel_slave COLLATE NOCASE, rel_slave LIMIT ? OFFSET ?`, append(args, f.Limit, f.Offset)...)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -448,6 +448,39 @@ func (s *Store) Matches(ctx context.Context, pairID int64, f MatchFilter) ([]Mat
|
||||
return out, total, rows.Err()
|
||||
}
|
||||
|
||||
// LetterStart is where the paths beginning with one (lowercased) character
|
||||
// start in the Matches listing of a filter.
|
||||
type LetterStart struct {
|
||||
Letter string `json:"letter"`
|
||||
Offset int `json:"offset"`
|
||||
}
|
||||
|
||||
// MatchLetters returns, in listing order, the offset of the first match for
|
||||
// each leading character of rel_slave, for jumping to an artist by letter.
|
||||
// Grouping on lower() of the first character keeps each group contiguous
|
||||
// under the NOCASE order Matches uses.
|
||||
func (s *Store) MatchLetters(ctx context.Context, pairID int64, f MatchFilter) ([]LetterStart, error) {
|
||||
w, args := matchWhere(pairID, f)
|
||||
rows, err := s.DB.QueryContext(ctx, `SELECT lower(substr(rel_slave, 1, 1)) AS l, COUNT(*) FROM matches WHERE `+w+
|
||||
` GROUP BY l ORDER BY l`, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := []LetterStart{}
|
||||
offset := 0
|
||||
for rows.Next() {
|
||||
var l string
|
||||
var n int
|
||||
if err := rows.Scan(&l, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, LetterStart{Letter: l, Offset: offset})
|
||||
offset += n
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// ExportMatches calls fn for every match of the filter, ordered by slave
|
||||
// path. Rows are read in pages so a slow download never holds the database
|
||||
// connection (scans need it) for longer than one page.
|
||||
|
||||
Reference in new issue
Block a user