Documentation
¶
Overview ¶
Package popularity ranks host content by a named, bounded policy over the signal plane's canonical window metrics (docs/popularity-policy.md). The viewer-reach policy uses:
reach = log10(1 + readers) engage = (reader_quality_sum + e0·ke) / (readers + ke) finish = (completers + f0·kf) / (readers + kf) approve = (positive + a0·ka) / (positive + negative + ka) revisit = returning_readers / (readers + 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. Each person's best measured edition quality contributes at most one unit; activity on multiple UTC days contributes at most one returning-reader observation. Priors smooth feedback: 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 metrics come from measured evidence, never from a rank. PolicyV2 instead weights bounded quality by eligible-population participation over immutable event-time access intervals.
Index ¶
- Constants
- Variables
- func Names() []string
- func WindowForPeriod(period string, now time.Time) (signal.Window, error)
- type Algorithm
- type AvailabilityProvider
- type Cache
- type Catalog
- type CatalogFunc
- type Config
- type GroupHit
- type Hit
- type MemoryCache
- type Policy
- type PopularResult
- type Priors
- type Ranker
- func (r *Ranker) Candidates(ctx context.Context, contentKind string, ids []string, window signal.Window) ([]Hit, error)
- func (r *Ranker) Groups(ctx context.Context, contentKind, groupKind string, window signal.Window) ([]GroupHit, error)
- func (r *Ranker) MaxDepth() int
- func (r *Ranker) Policy() Policy
- func (r *Ranker) Popular(ctx context.Context, contentKind string, window signal.Window, limit int) ([]Hit, error)
- func (r *Ranker) PopularResult(ctx context.Context, kind string, window signal.Window, limit int) (PopularResult, error)
- func (r *Ranker) Scores(ctx context.Context, contentKind string, ids []string, window signal.Window) (map[string]float64, error)
- func (r *Ranker) WithScope(scope signal.ActivityScope) *Ranker
- type RankingInfo
- type Source
- type Weights
Constants ¶
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 DefaultGroupCandidateLimit = 2000
DefaultGroupCandidateLimit bounds the ranked works a group listing aggregates; see docs/popularity-policy.md for what the bound means.
const DefaultMaxDepth = DefaultGroupCandidateLimit
DefaultMaxDepth is the deepest rank Popular serves by default; it equals DefaultGroupCandidateLimit, the deepest prefix Groups reads.
const DefaultMemoryCacheBytes = 32 << 20
DefaultMemoryCacheBytes is the budget of a MemoryCache built with none.
const DefaultName = "v1"
DefaultName is the ByName helper default; discovery separately defaults to v2.
const DefaultPeriod = "30d"
DefaultPeriod is the window a listing gets when it names none.
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.
var PolicyV2 = func() Policy { p := PolicyV1 p.Name = "v2" p.Algorithm = AlgorithmEligiblePopulation return p }()
PolicyV2 normalizes each historical access interval by its active eligible population. Its twenty pseudo-eligible nonreaders are a modest reliability heuristic, not a statistical confidence interval. Quality keeps v1's bounds.
Functions ¶
Types ¶
type Algorithm ¶ added in v0.98.0
type Algorithm string
Algorithm is the meaning of the ranking, independent of its policy version name. A newly named or tuned normalized policy retains its algorithm.
type AvailabilityProvider ¶ added in v0.98.0
type AvailabilityProvider interface {
Availability(context.Context, string, signal.Window, signal.ActivityScope) (signal.Availability, error)
}
AvailabilityProvider supplies immutable publication and access history for a common covered portion of a requested activity window.
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 {
Memberships(ctx context.Context, tenant, contentKind, groupKind string, contentIDs []string) (map[string][]contentref.GroupID, error)
}
Catalog is the join group popularity derives from: which groups (artist, series, tag, character, season, ...) content is in. ContentKit never records a signal against a group; a group ranks by its member contents. Memberships returns, per content id, the ids of the groups of groupKind effective on the content (it or any version); contents in none are absent. *groups.Store implements it.
type CatalogFunc ¶
type CatalogFunc func(ctx context.Context, tenant, contentKind, groupKind string, contentIDs []string) (map[string][]contentref.GroupID, error)
CatalogFunc adapts a function to Catalog.
func (CatalogFunc) Memberships ¶ added in v0.82.0
func (f CatalogFunc) Memberships(ctx context.Context, tenant, contentKind, groupKind string, contentIDs []string) (map[string][]contentref.GroupID, error)
type Config ¶
type Config struct {
Source Source
Policy Policy
// Catalog enables Groups. Optional.
Catalog Catalog
// Cache memoizes Popular and Groups under policy-named keys. Optional.
Cache Cache
CacheTTL time.Duration // default DefaultCacheTTL
// GroupCandidateLimit is the ranked-work bound of Groups (default
// DefaultGroupCandidateLimit). Part of the cache key.
GroupCandidateLimit int
// MaxDepth is the deepest rank Popular serves (default DefaultMaxDepth,
// or GroupCandidateLimit when larger); a larger limit is clamped, so a
// listing ends there. It bounds the rows one read returns and one cache
// entry holds. Must be at least GroupCandidateLimit.
MaxDepth int
Availability AvailabilityProvider
}
Config builds a Ranker. Hosts pass a policy name resolved through ByName or a Policy of their own; either is validated.
type GroupHit ¶ added in v0.82.0
type GroupHit struct {
GroupID contentref.GroupID
GroupKind string
Score float64 // summed member scores
ContentCount uint64 // ranked members inside the candidate bound
Readers uint64 // summed member readers (a person engaging with two members counts twice)
MeanScore float64 // Score / ContentCount
}
GroupHit is one ranked group, derived from its member works.
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 mean bounded measured quality per distinct qualified reader.
type MemoryCache ¶
type MemoryCache struct {
// contains filtered or unexported fields
}
MemoryCache is a process-local Cache with per-entry expiry and a byte budget (keys plus values). Past the budget the least recently used entries are evicted, so callers choosing keys can never grow it without bound.
func NewMemoryCache ¶
func NewMemoryCache(maxBytes int) *MemoryCache
NewMemoryCache returns an empty MemoryCache holding at most maxBytes; maxBytes <= 0 means DefaultMemoryCacheBytes.
func (*MemoryCache) Bytes ¶ added in v0.59.0
func (c *MemoryCache) Bytes() int
Bytes is the stored key and value bytes; never more than the budget.
func (*MemoryCache) Len ¶ added in v0.59.0
func (c *MemoryCache) Len() int
Len is the number of stored entries, expired ones included until touched.
type Policy ¶
type Policy struct {
Name string
// Empty preserves the original viewer-reach algorithm for callers.
Algorithm Algorithm
Weights Weights
Priors Priors
}
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) Normalized ¶ added in v0.98.0
Normalized reports whether this policy uses event-time eligible populations.
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 PopularResult ¶ added in v0.98.0
type PopularResult struct {
Hits []Hit `json:"hits"`
Ranking RankingInfo `json:"ranking"`
}
PopularResult includes provenance even when the ranking is empty.
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-readers 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 qualified readers in the window are absent, exactly as in Popular.
func (*Ranker) Groups ¶ added in v0.82.0
func (r *Ranker) Groups(ctx context.Context, contentKind, groupKind string, window signal.Window) ([]GroupHit, error)
Groups ranks the groups of one kind by the window popularity of their member works, joined through the Catalog over the top GroupCandidateLimit 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.
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 qualified readers rank; ties break on content id. limit is clamped to MaxDepth. The ranking is global and read in power-of-two prefixes, so a few cache entries per window serve every reader; hosts page by asking for offset+limit and slicing.
func (*Ranker) PopularResult ¶ added in v0.98.0
func (r *Ranker) PopularResult(ctx context.Context, kind string, window signal.Window, limit int) (PopularResult, error)
PopularResult returns ranked works with the actual policy and coverage.
type RankingInfo ¶ added in v0.98.0
type RankingInfo struct {
Policy string `json:"policy"`
Mode string `json:"mode"`
RequestedFrom string `json:"requested_from,omitempty"`
RequestedUntil string `json:"requested_until,omitempty"`
CoveredFrom string `json:"covered_from,omitempty"`
CoveredUntil string `json:"covered_until,omitempty"`
}
RankingInfo names the actual policy and common activity window, including an explicit unavailable result when no trusted audience history exists.
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; the engine's hub satisfies it. Every reference the Ranker builds carries Source.Tenant().