Documentation
¶
Overview ¶
Package popularity ranks host content by a named, bounded policy over the signal plane's canonical window metrics (docs/popularity-policy.md).
reach = log10(1 + viewers) engage = (viewer_engagement_sum + e0·ke) / (viewers + ke) finish = (completers + f0·kf) / (viewers + kf) approve = (positive + a0·ka) / (positive + negative + ka) revisit = returning_viewers / (viewers + kr) rank = reach × (1 + we·engage + wf·finish + wa·approve + wr·revisit)
Every quality term lies in [0, 1], so reach (distinct subjects) dominates and the multiplier is bounded by MaxQuality. Engagement averages sessions within each viewer first; each viewer contributes at most one engagement unit and one returning-viewer observation. Priors are pseudo-observations: one vote moves approve by at most 1/(ka+1). Time only selects the window; no term depends on when an observation happened inside it. The same formula runs in ClickHouse (RankExpr, global top-N) and in Go (Score, candidate sets), and public counts are the raw metrics, never derived from a rank.
Index ¶
- Constants
- Variables
- func Names() []string
- func SessionScore(covered, total, activeS uint32, secondsPerUnit float64) (score int16, completed bool)
- func WindowForPeriod(period string, now time.Time) (signal.Window, error)
- type Cache
- type Catalog
- type CatalogFunc
- type Config
- type Hit
- type MemoryCache
- type Policy
- type Priors
- type Ranker
- func (r *Ranker) Candidates(ctx context.Context, contentKind string, ids []string, window signal.Window) ([]Hit, error)
- func (r *Ranker) Policy() Policy
- func (r *Ranker) Popular(ctx context.Context, contentKind string, window signal.Window, limit int) ([]Hit, error)
- func (r *Ranker) Scores(ctx context.Context, contentKind string, ids []string, window signal.Window) (map[string]float64, error)
- func (r *Ranker) Taxonomy(ctx context.Context, contentKind, taxonomyKind string, window signal.Window) ([]TaxonomyHit, error)
- type SessionScorer
- type Source
- type TaxonomyHit
- type Weights
Constants ¶
const CompletionRatio = 0.9
CompletionRatio is the covered share of the selected version at which a session counts as completed.
const DefaultCacheTTL = 5 * time.Minute
DefaultCacheTTL bounds how stale a cached global ranking may be: the rollup behind it is daily, so minutes change nothing a reader would notice.
const DefaultName = "v1"
DefaultName is the policy a host gets when it configures none.
const DefaultPeriod = "30d"
DefaultPeriod is the window a listing gets when it names none.
const DefaultTaxonomyCandidateLimit = 2000
DefaultTaxonomyCandidateLimit bounds the ranked works a taxonomy listing aggregates; see docs/popularity-policy.md for what the bound means.
Variables ¶
var Periods = []string{"7d", "30d", "90d", "365d", "all"}
Periods are the only public popularity windows: literal whole-UTC-day windows with equal weight inside, "all" unbounded.
var PolicyV1 = Policy{ Name: "v1", Weights: Weights{Engagement: 0.35, Completion: 0.25, Approval: 0.40, Revisit: 0.10}, Priors: Priors{ Engagement: 0.40, EngagementWeight: 10, Completion: 0.30, CompletionWeight: 10, Approval: 0.75, ApprovalWeight: 5, RevisitWeight: 10, }, }
PolicyV1 is the qualified policy: weights and priors selected by the judged fixture recorded in docs/popularity-policy.md.
Functions ¶
Types ¶
type Cache ¶
type Cache interface {
Get(ctx context.Context, key string) ([]byte, bool)
Set(ctx context.Context, key string, value []byte, ttl time.Duration)
}
Cache memoizes global rankings. Optional: without one every read hits the signal plane. Keys carry the tenant, policy name, content kind, window and bounds, so two policies sharing one cache never read each other's entries.
type Catalog ¶
type Catalog interface {
Assignments(ctx context.Context, tenant, contentKind, taxonomyKind string, contentIDs []string) (map[string][]contentref.TaxonomyID, error)
}
Catalog is the host join port taxonomy popularity derives from: which taxonomy records (artist, series, tag, creator, character, season, ...) a work is assigned to. ContentKit never records a signal against a taxonomy id; a taxonomy record ranks by its member works. Assignments returns, per work id, the taxonomy ids of taxonomyKind effective on the work (work ∪ its versions); ids without assignments are absent.
type CatalogFunc ¶
type CatalogFunc func(ctx context.Context, tenant, contentKind, taxonomyKind string, contentIDs []string) (map[string][]contentref.TaxonomyID, error)
CatalogFunc adapts a function to Catalog.
func (CatalogFunc) Assignments ¶
func (f CatalogFunc) Assignments(ctx context.Context, tenant, contentKind, taxonomyKind string, contentIDs []string) (map[string][]contentref.TaxonomyID, error)
type Config ¶
type Config struct {
Source Source
Policy Policy
// Catalog enables Taxonomy. Optional.
Catalog Catalog
// Cache memoizes Popular and Taxonomy under policy-named keys. Optional.
Cache Cache
CacheTTL time.Duration // default DefaultCacheTTL
// TaxonomyCandidateLimit is the ranked-work bound of Taxonomy (default
// DefaultTaxonomyCandidateLimit). Part of the cache key.
TaxonomyCandidateLimit int
}
Config builds a Ranker. Hosts pass a policy name resolved through ByName or a Policy of their own; either is validated.
type Hit ¶
type Hit struct {
ContentID string
Score float64
signal.ContentMetrics
}
Hit is one ranked work: the policy score next to its raw window metrics. Public counts come from the metrics, never from the score.
func (Hit) MeanEngagement ¶
MeanEngagement is the mean session score in [0, 1]: ScoreSum / (100·Views).
type MemoryCache ¶
type MemoryCache struct {
// contains filtered or unexported fields
}
MemoryCache is a process-local Cache with per-entry expiry.
func NewMemoryCache ¶
func NewMemoryCache() *MemoryCache
NewMemoryCache returns an empty MemoryCache.
type Policy ¶
Policy is one named, immutable ranking. Changing a weight or prior is a new name, never an edit of an existing one: configuration and cache keys carry the name.
func ByName ¶
ByName resolves a configured policy. Unknown names are errors so a host refuses to start rather than ranking under a silent default.
func (Policy) MaxQuality ¶
MaxQuality is the largest multiplier the quality terms can reach.
func (Policy) RankExpr ¶
RankExpr renders Score as a ClickHouse expression over the window metric columns for signal.PopularOptions.RankExpr. Parameters are rendered as literals; nothing user-controlled reaches it.
type Priors ¶
type Priors struct {
Engagement float64
EngagementWeight float64
Completion float64
CompletionWeight float64
Approval float64
ApprovalWeight float64
RevisitWeight float64
}
Priors smooth each term with pseudo-observations: a prior mean in [0, 1] and its weight (>= 1) in pseudo-viewers or pseudo-votes.
type Ranker ¶
type Ranker struct {
// contains filtered or unexported fields
}
Ranker applies one policy to one tenant's signal plane.
func (*Ranker) Candidates ¶
func (r *Ranker) Candidates(ctx context.Context, contentKind string, ids []string, window signal.Window) ([]Hit, error)
Candidates scores a host-selected set of works (an artist's galleries, a search page, a tag) in Go over one Metrics read: score descending, content id ascending. Candidates without a view in the window are absent, exactly as in Popular.
func (*Ranker) Popular ¶
func (r *Ranker) Popular(ctx context.Context, contentKind string, window signal.Window, limit int) ([]Hit, error)
Popular returns the top limit works of one kind in a window, ranked by the policy inside ClickHouse (RankExpr). Only works with a view in the window rank; ties break on content id. The ranking is global, so one cache entry serves every reader; hosts page by asking for offset+limit and slicing.
func (*Ranker) Scores ¶
func (r *Ranker) Scores(ctx context.Context, contentKind string, ids []string, window signal.Window) (map[string]float64, error)
Scores is Candidates as content id → score.
func (*Ranker) Taxonomy ¶
func (r *Ranker) Taxonomy(ctx context.Context, contentKind, taxonomyKind string, window signal.Window) ([]TaxonomyHit, error)
Taxonomy ranks the taxonomy records of one kind by the window popularity of their member works, joined through the Catalog over the top TaxonomyCandidateLimit ranked works: score = summed member scores. The listing is approximate: a record with no member in that slice is absent and counts describe only the slice.
type SessionScorer ¶
type SessionScorer struct {
// SecondsPerUnit is the active time per covered unit at which a session
// counts as consumed rather than flipped through (8 for gallery pages).
// Zero means the units are seconds themselves: dwell is 1.
SecondsPerUnit float64
// CoveredKey names the Payload entry holding the covered units when the
// host keeps Progress as its resume anchor; empty reads Progress.
CoveredKey string
}
SessionScorer is the v1 session engagement score, a signal.Scorer for one content kind: coverage of the selected version times dwell.
coverage = min(1, covered / total) dwell = min(1, active_s / (covered × SecondsPerUnit)) score = round(100 × coverage × (0.6 + 0.4 × dwell)) ∈ [0, 100]
covered is the distinct units exposed (pages, seconds) and total the selected version's units (ProgressMax), so size never enters: a 6-page gallery read fully scores the same as a 200-page one read fully. Completed at CompletionRatio of the units. Non-view signals pass through unchanged.
type Source ¶
type Source interface {
Tenant() string
Popular(ctx context.Context, contentKind string, opts signal.PopularOptions) ([]signal.PopularHit, error)
Metrics(ctx context.Context, refs []signal.ContentRef, window signal.Window) (map[signal.ContentKey]signal.ContentMetrics, error)
}
Source reads one tenant's canonical window projections; contentkit.Hub satisfies it. Every reference the Ranker builds carries Source.Tenant().
type TaxonomyHit ¶
type TaxonomyHit struct {
TaxonomyID contentref.TaxonomyID
TaxonomyKind string
Score float64 // summed member scores
ContentCount uint64 // ranked members inside the candidate bound
Viewers uint64 // summed member viewers (a subject viewing two members counts twice)
MeanScore float64 // Score / ContentCount
}
TaxonomyHit is one ranked taxonomy record, derived from its member works.