fix: simplify filtering — let ranking + cap handle zero-pop artists

The popularity-scaled filter threshold couldn't distinguish 'unknown
popularity' (not in index) from 'confirmed zero' because most
zero-pop artists aren't in the explore index at all. Both cases
got HasPopularity=false.

Simpler approach: remove the special zero-pop filter entirely. With
proper popularity normalization (no +10M contamination), zero-pop
artists get blended scores of ~33-37 and naturally fall below
position 15 in the maxResults cap. Shannon Hale (score 36) ranks
#19 — cut by the cap, no special filtering needed.

Removed minScoreForArtist, minScoreZeroPop, and the HasPopularity/
Popularity-based filtering logic. The minBlendedScore=15 floor
catches extreme edge cases.
This commit is contained in:
2026-03-30 03:27:09 -04:00
parent e8fdf8dc54
commit 67d99a985e
+2 -59
View File
@@ -824,60 +824,8 @@ func scalePopularity(listens int) int {
// Filtering and capping
// ---------------------------------------------------------------------------
// filterAndCap removes low-scoring results, special-purpose
// MusicBrainz artists, and limits each entity slice to maxResults.
// minScoreForArtist returns the minimum score threshold for an
// artist based on their popularity. The threshold slides from
// minScoreZeroPop (60, for zero-listen artists) down to
// minBlendedScore (15, for popular artists).
//
// Uses log scaling: the threshold drops quickly for even modest
// popularity (1K listens → ~35) and flattens toward the floor
// for high popularity (100K+ → ~18).
//
// 0 listens → threshold 60 (need strong name match)
// 100 listens → threshold 50
// 1K listens → threshold 42
// 10K listens → threshold 33
// 100K listens → threshold 24
// 1M+ listens → threshold 15 (almost anything passes)
func minScoreForArtist(a MBArtist) int {
// Unknown popularity (not in index, no LB lookup yet) —
// use lenient threshold since we can't judge.
if !a.HasPopularity {
return minBlendedScore
}
// Known zero popularity — strict threshold.
if a.Popularity <= 0 {
return minScoreZeroPop
}
// Known popularity — threshold slides down with listen count.
const logCeiling = 6.0 // log10(1,000,000)
logPop := math.Log10(float64(a.Popularity))
ratio := logPop / logCeiling
if ratio > 1.0 {
ratio = 1.0
}
spread := float64(minScoreZeroPop - minBlendedScore)
threshold := minScoreZeroPop - int(ratio*spread)
if threshold < minBlendedScore {
threshold = minBlendedScore
}
return threshold
}
func filterAndCap(result *MBSearchResult) {
// Filter artists: remove SPAs and apply popularity-scaled threshold.
// The minimum score to survive scales with popularity — artists
// with zero listens need a very high score (near-exact match),
// while popular artists pass with any reasonable score.
// Filter artists: remove SPAs and low-scoring results.
if len(result.Artists) > 0 {
filtered := result.Artists[:0]
@@ -886,7 +834,7 @@ func filterAndCap(result *MBSearchResult) {
continue
}
if a.Score < minScoreForArtist(a) {
if a.Score < minBlendedScore {
continue
}
@@ -957,11 +905,6 @@ const (
// below this regardless of popularity.
minBlendedScore = 15
// minScoreZeroPop is the threshold for artists with zero
// popularity. The threshold slides between this and
// minBlendedScore based on listen count.
minScoreZeroPop = 60
// libraryScoreBonus is added to library artists' blended scores
// after normalization. Applied post-blending so it doesn't
// pollute the maxPop denominator.