Schéma SQLite — lmelp.db¶
Base de données SQLite embarquée dans l'APK, générée par scripts/export_mongo_to_sqlite.py depuis MongoDB masque_et_la_plume.
Conventions¶
- IDs : Strings (conversion depuis ObjectId MongoDB)
- Dates : Strings ISO 8601 (
YYYY-MM-DDTHH:MM:SSZ) - Notes : REAL (1.0 à 10.0)
- Booléens : INTEGER (0 ou 1)
- Durées : INTEGER (secondes)
Tables¶
emissions¶
Émissions de Le Masque et la Plume (émissions enrichies avec critiques et avis).
CREATE TABLE emissions (
id TEXT PRIMARY KEY, -- ObjectId MongoDB comme string
episode_id TEXT NOT NULL, -- Référence vers episodes.id
date TEXT NOT NULL, -- ISO 8601
duree INTEGER, -- Durée en secondes
animateur_id TEXT, -- Référence vers critiques.id (nullable)
nb_avis INTEGER DEFAULT 0, -- Nombre d'avis (précalculé)
has_summary INTEGER DEFAULT 0, -- A un résumé LLM (0/1)
created_at TEXT,
updated_at TEXT
);
episodes¶
Épisodes radio bruts (source RadioFrance).
CREATE TABLE episodes (
id TEXT PRIMARY KEY,
titre TEXT NOT NULL,
date TEXT, -- ISO 8601
description TEXT,
url TEXT, -- URL RadioFrance
duree INTEGER, -- Durée en secondes
masked INTEGER DEFAULT 0 -- Épisode masqué (0/1)
);
livres¶
Livres discutés dans les émissions.
CREATE TABLE livres (
id TEXT PRIMARY KEY,
titre TEXT NOT NULL,
auteur_id TEXT, -- Référence vers auteurs.id
auteur_nom TEXT, -- Dénormalisé pour éviter jointures
editeur TEXT,
url_babelio TEXT,
url_cover TEXT, -- URL directe de l'image de couverture (CDN Babelio ou Amazon)
created_at TEXT,
updated_at TEXT
);
auteurs¶
Auteurs des livres.
critiques¶
Les critiques et chroniqueurs de l'émission (25 personnes).
CREATE TABLE critiques (
id TEXT PRIMARY KEY,
nom TEXT NOT NULL,
animateur INTEGER DEFAULT 0, -- Est animateur (0/1)
nb_avis INTEGER DEFAULT 0 -- Nombre total d'avis (précalculé)
);
avis¶
Avis individuels des critiques sur les livres lors des émissions.
CREATE TABLE avis (
id TEXT PRIMARY KEY,
emission_id TEXT NOT NULL, -- Référence vers emissions.id
livre_id TEXT NOT NULL, -- Référence vers livres.id
critique_id TEXT NOT NULL, -- Référence vers critiques.id
note REAL, -- Note 1-10 (nullable)
commentaire TEXT,
-- Données dénormalisées (pour éviter jointures coûteuses)
livre_titre TEXT,
auteur_nom TEXT,
critique_nom TEXT,
-- Qualité de l'extraction
match_phase INTEGER, -- 1=exact, 2=partial, 3=similarité, null=non-matché
-- Section dans l'émission
section TEXT, -- "programme" | "coup_de_coeur" | null
created_at TEXT
);
emission_livres¶
Table de jointure émission ↔ livres discutés.
CREATE TABLE emission_livres (
emission_id TEXT NOT NULL,
livre_id TEXT NOT NULL,
PRIMARY KEY (emission_id, livre_id),
FOREIGN KEY (emission_id) REFERENCES emissions(id),
FOREIGN KEY (livre_id) REFERENCES livres(id)
);
palmares¶
Table précalculée à l'export. Livres classés par note moyenne.
CREATE TABLE palmares (
rank INTEGER NOT NULL, -- Rang (1 = meilleure note)
livre_id TEXT PRIMARY KEY,
titre TEXT NOT NULL,
auteur_nom TEXT,
note_moyenne REAL NOT NULL, -- Moyenne des notes
nb_avis INTEGER NOT NULL, -- Nombre d'avis avec note
nb_critiques INTEGER NOT NULL, -- Nombre de critiques distincts
calibre_in_library INTEGER DEFAULT 0, -- Livre présent dans Calibre (0/1)
calibre_lu INTEGER DEFAULT 0, -- Lu dans Calibre (0/1)
calibre_rating REAL, -- Note personnelle Calibre (1-10, nullable)
date_lecture TEXT, -- Date de lecture ISO YYYY-MM-DD (depuis Calibre custom_column_3)
FOREIGN KEY (livre_id) REFERENCES livres(id)
);
Colonne date_lecture : extraite depuis le champ Commentaire (custom_column_3) de Calibre via une regex \b(\d{2})/(\d{2})/(\d{4})\b. La date est convertie du format DD/MM/YYYY vers YYYY-MM-DD pour permettre un tri correct en SQLite.
recommendations¶
Table précalculée à l'export. Recommandations SVD collaborative filtering.
Filtrage dans l'app : l'écran Conseils effectue un LEFT JOIN avec
palmarespour exclure les livres déjà lus (calibre_in_library = 1 AND calibre_lu = 1). VoirRecommendationsDao.getRecommandationsNonLues().
CREATE TABLE recommendations (
rank INTEGER NOT NULL, -- Rang de recommandation
livre_id TEXT NOT NULL,
titre TEXT NOT NULL,
auteur_nom TEXT,
score_hybride REAL NOT NULL, -- Score final (70% SVD + 30% masque_mean)
svd_predict REAL, -- Prédiction SVD pure
masque_mean REAL, -- Moyenne avis Masque et la Plume
masque_count INTEGER, -- Nb avis Masque
PRIMARY KEY (livre_id),
FOREIGN KEY (livre_id) REFERENCES livres(id)
);
avis_critiques¶
Résumés LLM des émissions (générés par back-office-lmelp).
CREATE TABLE avis_critiques (
id TEXT PRIMARY KEY,
emission_id TEXT NOT NULL, -- Référence vers emissions.id (via episode)
episode_title TEXT,
episode_date TEXT,
summary TEXT, -- HTML table avec les avis détaillés
animateur TEXT,
critiques_json TEXT, -- JSON array des critiques présents
FOREIGN KEY (emission_id) REFERENCES emissions(id)
);
search_index (FTS5)¶
Index full-text pour la recherche. Table virtuelle SQLite FTS5.
CREATE VIRTUAL TABLE search_index USING fts5(
type, -- 'emission' | 'livre' | 'auteur' | 'critique'
ref_id, -- ID de l'entité référencée (TEXT)
content, -- Contenu indexé (titre + auteur + description...)
tokenize = 'unicode61 remove_diacritics 2'
);
Contenu indexé par type :
- emission : titre de l'épisode + description
- livre : titre + auteur_nom + editeur
- auteur : nom
- critique : nom + variantes
onkindle¶
Table précalculée à l'export. Livres présents sur la liseuse Kindle (tag onkindle dans Calibre), avec enrichissement depuis MongoDB (avis, palmares).
Filtre virtual library : si
LMELP_CALIBRE_VIRTUAL_LIBRARYest défini dans.env(ex:guillaume), seuls les livres portant ce tag Calibre en plus deonkindlesont inclus. Fallback avis : les livres présents dans Calibre mais non encore au palmarès (coups de cœur, nb_avis < 2) reçoivent leur note depuis la tableavisdirectement.
CREATE TABLE onkindle (
livre_id TEXT NOT NULL PRIMARY KEY, -- Correspondance avec livres.id si discuté au Masque
titre TEXT NOT NULL,
auteur_nom TEXT, -- Depuis livres.auteur_nom ou Calibre (fallback)
url_babelio TEXT, -- NULL si non discuté au Masque
url_cover TEXT, -- URL directe de l'image de couverture
calibre_lu INTEGER NOT NULL DEFAULT 0, -- Lu sur Kindle (0/1), depuis Calibre custom column
calibre_rating REAL, -- Note personnelle Calibre (1-10, nullable)
note_moyenne REAL, -- Note Masque et la Plume (NULL si non discuté)
nb_avis INTEGER NOT NULL DEFAULT 0 -- Nombre d'avis Masque (0 si non discuté)
);
Logique de construction (build_onkindle_table) :
1. Interroge Calibre pour récupérer les livres avec tag onkindle (+ tag vlib si configuré)
2. Croise avec palmares (par titre normalisé, insensible aux accents et apostrophes typographiques) pour note_moyenne / nb_avis
3. Si le livre n'est pas dans palmares : fallback sur avis (moyenne + count)
4. Croise avec livres pour url_babelio, url_cover et auteur_nom
5. Si auteur_nom est NULL après croisement : utilise l'auteur depuis Calibre
Tri dans l'app : le tri alphabétique est effectué en Kotlin via java.text.Collator(Locale.FRENCH, PRIMARY) (SQLite ne gère pas les accents correctement pour le français).
db_metadata¶
Métadonnées de la base (version, date d'export).
CREATE TABLE db_metadata (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
-- Valeurs insérées à l'export :
-- ('version', '42') -- PRAGMA user_version (timestamp Unix)
-- ('export_date', '2026-03-05') -- Date lisible
-- ('source_db', 'masque_et_la_plume')
-- ('nb_emissions', '173')
-- ('nb_livres', '1615')
-- ('nb_avis', '4100')
Index¶
-- Performances de navigation
CREATE INDEX idx_avis_emission ON avis(emission_id);
CREATE INDEX idx_avis_livre ON avis(livre_id);
CREATE INDEX idx_avis_critique ON avis(critique_id);
CREATE INDEX idx_emission_livres_emission ON emission_livres(emission_id);
CREATE INDEX idx_emission_livres_livre ON emission_livres(livre_id);
CREATE INDEX idx_livres_auteur ON livres(auteur_id);
CREATE INDEX idx_emissions_date ON emissions(date DESC);
CREATE INDEX idx_palmares_rank ON palmares(rank);
CREATE INDEX idx_recommendations_rank ON recommendations(rank);
Correspondance MongoDB → SQLite¶
| Collection MongoDB | Table SQLite | Transformations |
|---|---|---|
emissions |
emissions |
Calcul nb_avis, has_summary |
episodes |
episodes |
Direct |
livres |
livres |
Ajout auteur_nom (jointure auteurs) |
auteurs |
auteurs |
Direct |
critiques |
critiques |
Ajout nb_avis (précalculé) |
avis |
avis |
Conversion String IDs, dénormalisation noms |
avis_critiques |
avis_critiques |
Conversion metadata_source → colonnes |
| Calculé | palmares |
AVG(note) GROUP BY livre |
| Calculé | recommendations |
SVD sur matrice avis |
| Calculé | search_index |
FTS5 sur tous les textes |
| Calculé | emission_livres |
Jointure avis → (emission, livre) pairs |
| Calibre + MongoDB | onkindle |
Croisement tag Calibre + palmares/avis |
Taille estimée¶
| Table | Lignes | Taille estimée |
|---|---|---|
| emissions | 173 | ~100 KB |
| episodes | 227 | ~500 KB |
| livres | 1615 | ~300 KB |
| auteurs | 1114 | ~100 KB |
| critiques | 25 | ~5 KB |
| avis | 4100+ | ~2 MB |
| avis_critiques | 175 | ~5 MB |
| palmares | ~800 | ~100 KB |
| recommendations | ~500 | ~50 KB |
| search_index | ~3500 | ~500 KB |
| onkindle | ~28 | ~5 KB |
| Total | ~9-12 MB |