4.6 KiB
For You
Route
- URL:
GET /discover/for-you - Auth required: yes
- Controller:
App\Http\Controllers\Web\DiscoverController::forYou() - Entry service:
App\Services\Recommendations\RecommendationFeedResolver
High-level flow
For You is not a simple search sort.
It is a recommendation pipeline with engine selection, caching, layered candidate generation, reranking, and cursor pagination.
The controller:
- reads
limitandcursor - calls
RecommendationFeedResolver::getFeed() - converts feed items into the artwork card view model
- returns HTML or JSON depending on request type
Engine selection
RecommendationFeedResolver chooses between two implementations:
- V2:
App\Services\Recommendations\RecommendationServiceV2 - V1:
App\Services\Recommendations\PersonalizedFeedService
Selection is based on:
config('discovery.v2.enabled')- rollout percentage bucket for the current user
- explicit
algo_versionoverride
Cache model
Both engines use user_recommendation_cache.
At request time:
- Load cache row for
(user_id, algo_version) - Check cache version and
expires_at - If missing or stale, dispatch
RegenerateUserRecommendationCacheJob - If the row is empty, build fallback recommendations inline for the current request
This means the page is usually cache-backed, but it does not hard-fail if the cache is cold.
V2 pipeline
V2 is the richer layered engine.
Candidate layers
The candidate pool is blended from:
- personalized layer
- social layer
- trending layer
- exploration layer
- vector layer (only when V3/vector support is enabled and configured)
Default target ratios from config/discovery.php:
- personalized: 50%
- social: 20%
- trending: 20%
- exploration: 10%
Main V2 score
For each candidate row:
score
= (base_score * weight_base)
+ session_boost
+ social_boost
+ trending_boost
+ exploration_boost
+ creator_boost
+ vector_boost
- negative_penalty
- repetition_penalty
Where:
session_boostcomes from merged session/profile signalssocial_boostcomes from followed creators and artworks liked by followed creatorstrending_boostis built fromtrending_score_1h,trending_score_24h,trending_score_7dexploration_boostrewards fresh uploads, new creators, and unseen tagscreator_boostuses creator follower count plus artwork momentum metricsvector_boostcomes from visual similarity when vector mode is enablednegative_penaltyreflects hidden artworks and disliked tagsrepetition_penaltysuppresses creator and tag repetition inside one result page
Trending contribution inside V2
V2 combines the artwork's stored trending columns like this:
trendingBoost
= (trending_score_1h * weight_1h)
+ (trending_score_24h * weight_24h)
+ (trending_score_7d * weight_7d)
Then divides by 100 before merging into the final score.
Negative signals
V2 reads user_negative_signals and applies:
- hidden artwork exclusion
- disliked tag penalty
Supporting data sources
V2 reads from:
user_recommendation_cache- session/profile signal builders
artworksartwork_statsartwork_similaritiesartwork_embeddingsand vector service, when enableduser_followersartwork_favouritesuser_negative_signals
V1 pipeline
V1 is simpler and category-affinity driven.
It reads user_interest_profiles, then scores candidates with a weighted blend:
score = (w1 * affinity)
+ (w2 * recency)
+ (w3 * popularity)
+ (w4 * novelty)
Cold start falls back to a blend of popular artworks and artwork_similarities seeds.
Background jobs and schedules
Directly relevant
RegenerateUserRecommendationCacheJob- dispatched on demand when cache is missing or stale
Support jobs for candidate quality
RecBuildItemPairsFromFavouritesJobevery 4 hoursRecComputeSimilarByTagsJobdaily at 02:00RecComputeSimilarByBehaviorJobdaily at 02:15RecComputeSimilarHybridJobdaily at 02:30analytics:aggregate-discovery-feedbackdaily at 03:25
These jobs do not directly render the page, but they improve the offline inputs and behavioral data used by the recommender.
Cache behavior
- V1 cache TTL default: 60 minutes
- V2 cache TTL default: 15 minutes
Cursor pagination is offset-based under the hood.
Notes
For Youis the most configuration-sensitive page in this set.- What a given user sees can differ by rollout bucket,
algo_version, cache state, and whether V2/V3 features are enabled. - If you are debugging a single user's page, inspect
RecommendationFeedResolver::inspectDecision()first.