Data model¶
The shape of a library¶
library/
library.db SQLite index
collections/
my-games/games.pgn the actual games, appended in import order
opening-trees/games.pgn
studies/
prep/study.json a manifest naming the collections in this folder
books/ PDFs, by id
The PGN files are the library. library.db records, for each game, the file it is in
and the byte range it occupies — so reading a game is a seek and a read, and losing the
index costs you search rather than games.
Tables¶
collections¶
id, name (unique), kind, created_at.
kind is games, studies or openings, decided when the collection is created and
fixed thereafter. See The game database.
games¶
The index row for one game. Beyond the location fields (path, byte_offset,
byte_length, signature):
Columns |
From |
|---|---|
|
the standard PGN seven-tag roster and its usual companions |
|
annotation provenance; |
|
ChessBase’s richer tag set — see Importing games |
|
derived at import from |
|
the opening moves, for naming openings without replaying the game |
|
where it came from and when |
source_id is unique where present (lichess:<id>, chesscom:<id>). Where a file
carries no such id, signature — a hash over the identifying tags and the moves —
catches duplicates instead.
game_collections¶
(game_id, collection_id, added_at) — the extra shelves a game sits on. games.collection_id
remains the owning collection; this table holds only the links, so every query that
existed before it still means what it did.
A collection contains the games it owns plus the games linked onto it, which is how
search(collection=…), the collection counts and the PGN export all read it. Both
columns cascade on delete, and deleting a collection first hands any game it owns but
another collection also holds to that other collection.
positions¶
(hash, game_id, ply, next_san). The hash is a transposition key: the same position
reached by a different move order hashes the same, which is what makes the position
search useful. Built per collection by the indexer.
repertoires¶
id, name, color, data, updated_at. data is JSON:
{
"lines": [
{"moves": ["e4", "e5", "Nf3"], "fen": "…", "due": 0, "interval": 0, "successes": 0}
]
}
due is an epoch milliseconds timestamp; interval is the current spacing in days.
settings, explorer_cache, import_batches, import_members¶
Preferences (including the lichess token, which never leaves the server), cached online explorer answers, and the batch records that make an import undoable.
PGN handling¶
Reading is deliberately split. The server splits files, reads tags and extracts the main line — it never needs to understand chess to do that. The browser has a full rules engine and does the rest: variations, legality, SAN, and the move tree.
Comment commands¶
[%…]commands inside comments are lifted out of the prose, shown beside the moves they belong to, and written back on save:Command
Meaning
[%eval …]the engine’s score for this move
[%evp a,b,c]the main line’s evaluation per ply, on the root
[%cal …]arrows you drew
[%csl …]circled squares
[%clk],[%emt],[%mdl],[%tqu]clocks, elapsed time, ChessBase medals and training positions — preserved, not interpreted