Skip to content

Database Documentation

Last Updated: 2026-05-08

The backend uses SQLite with Doctrine ORM for persistence.

Overview

  • Engine: SQLite (single file)
  • ORM: Doctrine ORM with PHP 8 attribute mapping
  • Location: backend/var/data.db
  • Tables: 8

Why SQLite?

  • No separate database server required
  • Single file, easy to backup and reset
  • Fast for the team's scale
  • Zero configuration

Schema

Tables (8)

  1. category — Thematic lists with digest settings
  2. knowledge — Knowledge items with AI highlights
  3. digest — Generated digest reports
  4. digest_delivery — Per-user delivery tracking
  5. subscription — User category subscriptions
  6. digest_knowledge — ManyToMany join table (Digest ↔ Knowledge)
  7. doctrine_migration_versions — Migration tracking
  8. sqlite_sequence — Auto-increment tracking

Entity Relationship Diagram


Table Definitions

category

ColumnTypeConstraints
idINTEGERPK, AUTOINCREMENT
nameVARCHAR(255)NOT NULL
descriptionVARCHAR(255)NULLABLE
iconVARCHAR(50)DEFAULT '📁'
target_groupsTEXT (JSON)NULLABLE
notion_idVARCHAR(255)NULLABLE
last_synced_atDATETIMENULLABLE
notion_last_edited_atDATETIMENULLABLE
created_atDATETIMENOT NULL
updated_atDATETIMENOT NULL
is_activeBOOLEANDEFAULT 1
sort_orderINTEGERDEFAULT 0
digest_enabledBOOLEANDEFAULT 0
digest_frequencyVARCHAR(20)NULLABLE (enum)
last_digest_atDATETIMENULLABLE
digest_dayINTEGERNULLABLE
digest_timeVARCHAR(5)NULLABLE

knowledge

ColumnTypeConstraints
idINTEGERPK, AUTOINCREMENT
titleVARCHAR(255)NOT NULL
contentTEXTNOT NULL
tagsTEXT (JSON)NULLABLE
target_groupsTEXT (JSON)NULLABLE
user_idVARCHAR(255)NULLABLE
statusVARCHAR(50)DEFAULT 'pending'
urlVARCHAR(500)NULLABLE
url_metadataTEXT (JSON)NULLABLE
source_messageTEXT (JSON)NULLABLE
attachmentsTEXT (JSON)NULLABLE
summariesTEXT (JSON)NULLABLE
highlightTEXTNULLABLE
created_atDATETIMENOT NULL
updated_atDATETIMENOT NULL
notion_idVARCHAR(255)NULLABLE
notion_urlVARCHAR(2048)NULLABLE
last_synced_atDATETIMENULLABLE
notion_last_edited_atDATETIMENULLABLE
category_idINTEGERFK → category(id) ON DELETE SET NULL

Indexes: category_id, notion_id, status, user_id

digest

ColumnTypeConstraints
idINTEGERPK, AUTOINCREMENT
titleVARCHAR(255)NOT NULL
contentTEXTNULLABLE
periodVARCHAR(50)DEFAULT 'Weekly'
statusVARCHAR(50)DEFAULT 'Done'
statisticsTEXT (JSON)NULLABLE
knowledge_highlightsTEXT (JSON)NULLABLE
generated_byVARCHAR(255)NULLABLE
notion_idVARCHAR(255)NULLABLE
notion_urlVARCHAR(2048)NULLABLE
last_synced_atDATETIMENULLABLE
created_atDATETIMENOT NULL
updated_atDATETIMENOT NULL
category_idINTEGERFK → category(id) NOT NULL

digest_delivery

ColumnTypeConstraints
idINTEGERPK, AUTOINCREMENT
user_idVARCHAR(255)NOT NULL
delivery_channelVARCHAR(50)DEFAULT 'slack'
statusVARCHAR(50)DEFAULT 'pending'
created_atDATETIMENOT NULL
sent_atDATETIMENULLABLE
error_messageTEXTNULLABLE
retry_countINTEGERDEFAULT 0
digest_idINTEGERFK → digest(id) ON DELETE CASCADE NOT NULL

subscription

ColumnTypeConstraints
idINTEGERPK, AUTOINCREMENT
user_idVARCHAR(255)NOT NULL
slack_user_nameVARCHAR(255)NULLABLE
is_activeBOOLEANDEFAULT 1
subscribed_atDATETIMENOT NULL
unsubscribed_atDATETIMENULLABLE
notion_idVARCHAR(255)NULLABLE
last_synced_atDATETIMENULLABLE
category_idINTEGERFK → category(id) ON DELETE CASCADE NOT NULL

Unique constraint: uq_user_category_subscription on (user_id, category_id)

Indexes: user_id, category_id, is_active

digest_knowledge (join table)

ColumnTypeConstraints
digest_idINTEGERFK → digest(id) ON DELETE CASCADE
knowledge_idINTEGERFK → knowledge(id) ON DELETE CASCADE

Primary key: (digest_id, knowledge_id)


Schema Management

The schema was created using doctrine:schema:create (not migrations). To recreate:

bash
cd backend
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console doctrine:schema:create

To check schema validity:

bash
php bin/console doctrine:schema:validate

Direct SQLite Access

bash
cd backend/var
sqlite3 data.db

# List tables
.tables

# Describe table
.schema knowledge

# Query
SELECT * FROM knowledge LIMIT 10;

# Count records
SELECT 'knowledge' as tbl, COUNT(*) FROM knowledge
UNION ALL SELECT 'category', COUNT(*) FROM category
UNION ALL SELECT 'digest', COUNT(*) FROM digest
UNION ALL SELECT 'subscription', COUNT(*) FROM subscription;

.quit

Backup and Restore

bash
# Backup
cp backend/var/data.db backend/var/data.db.backup

# Export to SQL
sqlite3 backend/var/data.db .dump > backup.sql

# Restore
cp backend/var/data.db.backup backend/var/data.db