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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user