Files
2026-04-18 17:02:56 +02:00

4.3 KiB

Rising

Route

  • URL: GET /discover/rising
  • Controller: App\Http\Controllers\Web\DiscoverController::rising()
  • Service: App\Services\ArtworkSearchService::discoverRising()
  • RSS feed: GET /rss/discover/rising via App\Http\Controllers\RSS\DiscoverFeedController::rising()

What the page reads

Like most Discover surfaces, this page ranks via Meilisearch and then hydrates the result IDs from MySQL for presentation.

If the search-backed query throws or returns no items, the controller falls back to a direct MySQL query against artworks + artwork_stats.

If the page receives a non-empty result set but every item has zero heat_score and zero engagement_velocity, it switches to a low-signal fallback policy instead of pretending that the zero-heat order is meaningful.

The RSS Rising feed now follows the same low-signal policy and the same adaptive lookback window, so it does not drift to a stale zero-heat ordering when recent engagement is sparse.

Primary ranking fields:

  • heat_score
  • engagement_velocity
  • published_at_ts as the final recency tie-breaker

Search query

discoverRising() uses:

  • filter: is_public = true AND is_approved = true AND created_at >= cutoff
  • sort:
    • heat_score:desc
    • engagement_velocity:desc
    • published_at_ts:desc

The cutoff comes from the same adaptive time-window service used by Trending.

Rising formula

heat_score is produced by App\Console\Commands\RecalculateHeatCommand.

Current formula:

raw_heat
  = ((views_delta * 1)
     + (downloads_delta * 3)
     + (favourites_delta * 6)
     + (comments_delta * 8)
     + (shares_delta * 12)) / window_hours

age_factor
  = 1 / (1 + hours_since_upload / 24)

heat_score
  = raw_heat * age_factor

The heat command smooths deltas over a trailing lookback window, rather than relying only on the last single hour.

That matters on low-traffic periods, because a pure 1-hour delta often collapses to zero for almost every artwork.

An artwork still needs at least two snapshots inside that window for the smoothed heat delta to count. A single snapshot without an earlier baseline does not count as momentum.

The views_1h, downloads_1h, favourites_1h, comments_1h, and shares_1h columns are still stored from the previous-hour comparison for diagnostics and dashboards.

Data sources

The page depends on:

  • artwork_metric_snapshots_hourly
  • artwork_stats.heat_score
  • artwork_stats.engagement_velocity
  • artwork publish timestamps

In zero-signal periods, the fallback policy also uses a 24-hour snapshot delta rollup from artwork_metric_snapshots_hourly and then falls back to published_at DESC.

engagement_velocity is not part of the heat command. It comes from the ranking engine and acts as a secondary momentum signal.

Intended background jobs

The intended pipeline is:

  1. nova:metrics-snapshot-hourly
    • captures hourly totals into artwork_metric_snapshots_hourly
  2. nova:recalculate-heat
    • computes heat_score from snapshot deltas
  3. Meilisearch picks up the updated score after indexing

Runtime schedule

Rising depends on two active Laravel 11 runtime jobs in routes/console.php:

  • nova:metrics-snapshot-hourly
  • nova:recalculate-heat

If either one disappears from php artisan schedule:list, Rising will quickly drift toward stale or low-signal ordering.

Active jobs that still affect Rising

  • nova:recalculate-rankings --sync-rank-scores every 30 minutes updates engagement_velocity
  • skinbase:flush-redis-stats every 5 minutes keeps all-time stats fresher

Cache behavior

  • Cache key: discover.rising.{windowDays}d.{page}
  • TTL: 120 seconds

If Meilisearch sort settings are missing or the search result is empty, the controller falls back to the DB query instead of returning an empty page or a 500.

Notes

  • If Rising looks frozen while Trending moves, the first place to check is whether nova:metrics-snapshot-hourly and nova:recalculate-heat are actually being executed in production.
  • The page no longer uses GridFiller, so it should not pull in unrelated older artworks when the real result set is thin.
  • If all heat and velocity values are zero, Rising intentionally behaves like a low-signal discovery feed: recent activity in the last 24 hours first, then newest published artworks.