Cascading Deletes: The schema uses ForeignKey.CASCADE strictly for the LimitGroup -> LimitedApp and LimitGroup -> SnoozeHistoryEntity relationships. This ensures that deleting a user-defined limit group cleans up all associated configuration and history immediately.
Logical Keys: Most other relationships (e.g., AppMetadata -> DailyAppUsageRecord) rely on logical string keys (package_name) rather than strict SQL constraints. This allows usage data to persist even if an app is temporarily uninstalled (AppMetadata removed).
Time-Series Optimization: Almost every table includes a date_string column (“YYYY-MM-DD”) with an associated @Index. This allows for efficient WHERE date_string = :today queries, which power 90% of the UI.
Uniqueness:
daily_app_usage: (package_name, date_string) is unique. Upsert strategies are used to update usage counters.
daily_insights: (date_string, insight_key) is unique.
Raw Data: Tables like raw_app_events and notifications grow indefinitely. The Dao includes methods like deleteEventsOlderThan to prune data based on timestamps, preventing database bloat on mobile devices.
Materialized Views: user_journey acts as a cache. It can be completely wiped and rebuilt from raw_app_events and unlock_sessions if the schema changes or data corruption occurs.
Upserts: The schema heavily utilizes @Upsert (or OnConflictStrategy.REPLACE) for statistical tables (daily_app_usage, app_categories). This ensures that background sync jobs and foreground UI updates do not cause Primary Key collisions.