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
|
// 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) {
|
func filterAndCap(result *MBSearchResult) {
|
||||||
// Filter artists: remove SPAs and apply popularity-scaled threshold.
|
// Filter artists: remove SPAs and low-scoring results.
|
||||||
// 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.
|
|
||||||
if len(result.Artists) > 0 {
|
if len(result.Artists) > 0 {
|
||||||
filtered := result.Artists[:0]
|
filtered := result.Artists[:0]
|
||||||
|
|
||||||
@@ -886,7 +834,7 @@ func filterAndCap(result *MBSearchResult) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.Score < minScoreForArtist(a) {
|
if a.Score < minBlendedScore {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,11 +905,6 @@ const (
|
|||||||
// below this regardless of popularity.
|
// below this regardless of popularity.
|
||||||
minBlendedScore = 15
|
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
|
// libraryScoreBonus is added to library artists' blended scores
|
||||||
// after normalization. Applied post-blending so it doesn't
|
// after normalization. Applied post-blending so it doesn't
|
||||||
// pollute the maxPop denominator.
|
// pollute the maxPop denominator.
|
||||||
|
|||||||
Reference in New Issue
Block a user