#
Analytics Architecture
#
Stats Storage
Every time a video's statistics are fetched (from any flow — start tracking, nightly refresh, blogger batch), a new row is inserted into video_statistics:
-- video_statistics schema
video_id UUID -- FK → videos.id
external_video_id TEXT -- Platform video ID
views BIGINT
likes BIGINT
comments BIGINT
reposts BIGINT
saves BIGINT
created_at TIMESTAMPTZ
This is an append-only table — each fetch creates a new snapshot, not an update.
#
Materialized Views
Two materialized views compute incremental analytics from the raw snapshots:
#
mv_video_daily_increments
Computes the delta between consecutive video_statistics rows within a day:
views_delta,likes_delta,comments_delta,reposts_delta- Grouped by
external_video_id+ date
#
mv_video_weekly_increments
Aggregates daily increments into weekly summaries.
#
videos_with_statistics_fast
A fast read view that joins videos with the latest video_statistics row per video. Used heavily by the dashboard list and Google Sheets export.
#
Data Flow
graph TB
A[RapidAPI fetch] -->|queue-processor| B[video_statistics INSERT]
B --> C[mv_video_daily_increments]
C --> D[mv_video_weekly_increments]
B --> E[videos_with_statistics_fast]
F[Dashboard API] -->|reads| E
F -->|reads| C
F -->|reads| D
G[Google Sheets export] -->|reads| E
#
Refresh Schedule
Materialized views are refreshed periodically by the materialized-views-refresh worker (or equivalent scheduled function). The refresh is triggered after significant write batches to keep data current without refreshing on every insert.
#
Totals vs Increments
The system supports both modes:
This distinction matters for campaign analytics — a video added mid-campaign shows only the increment from when tracking started, not historical totals.