From d8557fcfca4e65857bc8d89fc733ac805aeb8258 Mon Sep 17 00:00:00 2001 From: jason Date: Thu, 12 Mar 2026 07:37:20 -0500 Subject: [PATCH] database --- DATABASE.md | 274 ++++++++++++++++++---------------------------------- 1 file changed, 94 insertions(+), 180 deletions(-) diff --git a/DATABASE.md b/DATABASE.md index 673c2af..3f54987 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -2,221 +2,135 @@ ## Overview -This document describes the clean database schema for BREEDR. **NO migrations** - fresh installs create the correct schema automatically. +This document describes the database schema for BREEDR. The application uses SQLite via `better-sqlite3`. + +**Safe Migrations:** The schema is maintained in `server/db/init.js`. On startup, the application automatically ensures all tables exist and all necessary columns are present using safe `ALTER TABLE` guards. ## Schema Design ### Core Principle: Parents Table Approach -The `dogs` table **does NOT have sire/dam columns**. Parent relationships are stored in the separate `parents` table. This design: -- Keeps the schema clean and normalized -- Allows flexible parent relationships -- Supports future extensions (multiple sires, surrogates, etc.) +The `dogs` table **does NOT have sire/dam columns**. Parent relationships are stored in a separate `parents` table. This design: +- Keeps the core dog data normalized. +- Allows for flexible parent relationships. +- Supports historical mapping where ancestors might not have full profiles initially. + +--- ## Tables ### dogs +Core registry for all dogs (both in-kennel and external). -Core registry for all dogs. - -```sql -CREATE TABLE dogs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - registration_number TEXT UNIQUE, - breed TEXT NOT NULL, - sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), - birth_date DATE, - color TEXT, - microchip TEXT, - photo_urls TEXT, -- JSON array - notes TEXT, - litter_id INTEGER, -- Links to litters table - is_active INTEGER DEFAULT 1, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (litter_id) REFERENCES litters(id) ON DELETE SET NULL -); -``` - -**Important:** NO `sire_id` or `dam_id` columns! +| Column | Type | Description | +|---|---|---| +| id | INTEGER | Primary Key | +| name | TEXT | Dog's call name or registered name | +| registration_number | TEXT | Unique registration (AKC, etc.) | +| breed | TEXT | Breed name | +| sex | TEXT | 'male' or 'female' | +| birth_date | TEXT | Date string | +| color | TEXT | Coat color/markings | +| microchip | TEXT | Identification number | +| litter_id | INTEGER | FK to `litters.id` | +| is_active | INTEGER | 1 = Active in kennel, 0 = Retired/Old | +| is_champion | INTEGER | 1 = Titled champion | +| is_external | INTEGER | 1 = External dog (pedigree only), 0 = Kennel dog | +| chic_number | TEXT | CHIC certification number | +| age_at_death | TEXT | Age if deceased | +| cause_of_death | TEXT | Cause if deceased | +| photo_urls | TEXT | JSON array of photo paths | +| notes | TEXT | General observations | ### parents - -Stores sire/dam relationships. +Stores sire/dam relationships for every dog. ```sql CREATE TABLE parents ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - dog_id INTEGER NOT NULL, -- The puppy - parent_id INTEGER NOT NULL, -- The parent + id INTEGER PRIMARY KEY AUTOINCREMENT, + dog_id INTEGER NOT NULL, -- The puppy/child + parent_id INTEGER NOT NULL, -- The parent dog parent_type TEXT NOT NULL CHECK(parent_type IN ('sire', 'dam')), - FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE, - FOREIGN KEY (parent_id) REFERENCES dogs(id) ON DELETE CASCADE, - UNIQUE(dog_id, parent_type) -- One sire, one dam per dog + FOREIGN KEY (dog_id) REFERENCES dogs(id), + FOREIGN KEY (parent_id) REFERENCES dogs(id), + UNIQUE(dog_id, parent_type) ); ``` -### litters +### breeding_records +Tracks mating attempts/plans. -Breeding records and litter tracking. +| Column | Type | Description | +|---|---|---| +| sire_id | INTEGER | FK to `dogs.id` | +| dam_id | INTEGER | FK to `dogs.id` | +| breeding_date | TEXT | Date of mating | +| due_date | TEXT | Estimated whelp date | +| conception_method | TEXT | 'natural', 'ai', 'frozen', 'surgical' | + +### litters +Tracks successfully produced litters. ```sql CREATE TABLE litters ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - sire_id INTEGER NOT NULL, - dam_id INTEGER NOT NULL, - breeding_date DATE NOT NULL, - whelping_date DATE, - puppy_count INTEGER DEFAULT 0, - notes TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (sire_id) REFERENCES dogs(id) ON DELETE CASCADE, - FOREIGN KEY (dam_id) REFERENCES dogs(id) ON DELETE CASCADE + id INTEGER PRIMARY KEY AUTOINCREMENT, + breeding_id INTEGER, -- Optional link to breeding_record + sire_id INTEGER NOT NULL, + dam_id INTEGER NOT NULL, + whelp_date TEXT, + total_count INTEGER DEFAULT 0, + male_count INTEGER DEFAULT 0, + female_count INTEGER DEFAULT 0, + stillborn_count INTEGER DEFAULT 0, + notes TEXT, + FOREIGN KEY (breeding_id) REFERENCES breeding_records(id), + FOREIGN KEY (sire_id) REFERENCES dogs(id), + FOREIGN KEY (dam_id) REFERENCES dogs(id) ); ``` -### health_records +### health_records (OFA & General) +Tracks medical tests and certifications (Hips, Elbows, Heart, Eyes, etc). -Health tests, vaccinations, exams, treatments. +| Column | Type | Description | +|---|---|---| +| record_type | TEXT | 'test', 'vaccination', 'exam', 'treatment', 'certification' | +| test_type | TEXT | 'hip_ofa', 'eye_caer', etc. | +| ofa_result | TEXT | Official rating (Excellent, Good, etc.) | +| ofa_number | TEXT | Certification number | +| expires_at | TEXT | Expiry for annual tests | +| document_url | TEXT | Link to PDF or image scan | + +### genetic_tests +DNA panel results (Embark, etc). ```sql -CREATE TABLE health_records ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - dog_id INTEGER NOT NULL, - record_type TEXT NOT NULL CHECK(record_type IN ('test', 'vaccination', 'exam', 'treatment', 'certification')), - test_name TEXT, - test_date DATE NOT NULL, - result TEXT, - document_url TEXT, - notes TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE +CREATE TABLE genetic_tests ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + dog_id INTEGER NOT NULL, + test_provider TEXT, -- Embark, PawPrint, etc. + marker TEXT NOT NULL, -- PRA1, ICH1, etc. + result TEXT NOT NULL CHECK(result IN ('clear', 'carrier', 'affected', 'not_tested')), + FOREIGN KEY (dog_id) REFERENCES dogs(id) ); ``` ### heat_cycles +Female heat cycle tracking. -Female heat cycle tracking for breeding timing. +### settings +Global kennel configuration (Single-row table). +- `kennel_name`, `kennel_tagline`, `kennel_address`, `owner_name`, etc. -```sql -CREATE TABLE heat_cycles ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - dog_id INTEGER NOT NULL, - start_date DATE NOT NULL, - end_date DATE, - progesterone_peak_date DATE, - breeding_date DATE, - breeding_successful INTEGER DEFAULT 0, - notes TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE -); -``` +--- -### traits +## Migration Policy -Genetic trait tracking and inheritance. +BREEDR uses a **Migration-Free** sync approach: +1. `init.js` defines the latest `CREATE TABLE` statements. +2. An `ADD COLUMN` loop checks for existing columns. +3. If a column is missing, it is injected via `ALTER TABLE`. +4. This ensures users can pull latest code and restart without losing data. -```sql -CREATE TABLE traits ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - dog_id INTEGER NOT NULL, - trait_category TEXT NOT NULL, - trait_name TEXT NOT NULL, - trait_value TEXT NOT NULL, - inherited_from INTEGER, -- Parent dog ID - notes TEXT, - FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE, - FOREIGN KEY (inherited_from) REFERENCES dogs(id) ON DELETE SET NULL -); -``` - -## API Usage - -### Creating a Dog with Parents - -```javascript -POST /api/dogs -{ - "name": "Puppy Name", - "breed": "Breed Name", - "sex": "male", - "sire_id": 5, // Parent male dog ID - "dam_id": 8, // Parent female dog ID - "litter_id": 2 // Optional: link to litter -} -``` - -The API route automatically: -1. Inserts the dog into `dogs` table (without sire/dam columns) -2. Creates entries in `parents` table linking to sire and dam - -### Querying Parents - -```sql --- Get a dog's parents -SELECT p.parent_type, d.* -FROM parents p -JOIN dogs d ON p.parent_id = d.id -WHERE p.dog_id = ?; - --- Get a dog's offspring -SELECT d.* -FROM dogs d -JOIN parents p ON d.id = p.dog_id -WHERE p.parent_id = ?; -``` - -## Fresh Install - -For a fresh install: - -1. **Delete the old database** (if upgrading): - ```bash - rm data/breedr.db - ``` - -2. **Start the server** - it will create the correct schema automatically: - ```bash - npm run dev - ``` - -3. **Verify the schema**: - ```bash - sqlite3 data/breedr.db ".schema dogs" - ``` - - You should see `litter_id` but **NO** `sire_id` or `dam_id` columns. - -## Troubleshooting - -### "no such column: weight" or "no such column: sire_id" - -**Solution:** Your database has an old schema. Delete it and let the app recreate it: - -```bash -rm data/breedr.db -npm run dev -``` - -### Parent relationships not saving - -Check server logs. You should see: -``` -✓ Dog inserted with ID: 123 - Adding sire relationship: dog 123 -> sire 5 - ✓ Sire relationship added - Adding dam relationship: dog 123 -> dam 8 - ✓ Dam relationship added -``` - -If relationships aren't being created, check that `sire_id` and `dam_id` are being sent in the API request. - -## Database Files - -- `server/db/init.js` - Creates clean schema, no migrations -- `server/routes/dogs.js` - Handles parent relationships via `parents` table -- `server/index.js` - Initializes database on startup - -**NO MIGRATIONS!** The init file is the source of truth. +--- +*Last Updated: March 12, 2026*