223 lines
5.3 KiB
Markdown
223 lines
5.3 KiB
Markdown
|
|
# BREEDR Database Schema
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
This document describes the clean database schema for BREEDR. **NO migrations** - fresh installs create the correct schema automatically.
|
||
|
|
|
||
|
|
## 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.)
|
||
|
|
|
||
|
|
## Tables
|
||
|
|
|
||
|
|
### dogs
|
||
|
|
|
||
|
|
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!
|
||
|
|
|
||
|
|
### parents
|
||
|
|
|
||
|
|
Stores sire/dam relationships.
|
||
|
|
|
||
|
|
```sql
|
||
|
|
CREATE TABLE parents (
|
||
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
|
|
dog_id INTEGER NOT NULL, -- The puppy
|
||
|
|
parent_id INTEGER NOT NULL, -- The parent
|
||
|
|
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
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### litters
|
||
|
|
|
||
|
|
Breeding records and litter tracking.
|
||
|
|
|
||
|
|
```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
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### health_records
|
||
|
|
|
||
|
|
Health tests, vaccinations, exams, treatments.
|
||
|
|
|
||
|
|
```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
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### heat_cycles
|
||
|
|
|
||
|
|
Female heat cycle tracking for breeding timing.
|
||
|
|
|
||
|
|
```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
|
||
|
|
|
||
|
|
Genetic trait tracking and inheritance.
|
||
|
|
|
||
|
|
```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.
|