244 lines
7.4 KiB
Markdown
244 lines
7.4 KiB
Markdown
# Items Table Expansion - Migration Plan
|
|
|
|
## Goal
|
|
Expand the items table to support efficient queries for an interactive map and media wiki server.
|
|
|
|
## Database Design Strategy
|
|
|
|
We'll use a **hybrid approach**:
|
|
1. **Commonly queried fields** → Direct columns in `items` table
|
|
2. **One-to-many/many-to-many relationships** → Separate normalized tables
|
|
3. **Complex nested data** → Keep in JSON `data` column
|
|
|
|
---
|
|
|
|
## Phase 1: Add Core Columns to Items Table
|
|
|
|
### New Columns for `items` table:
|
|
```sql
|
|
ALTER TABLE items ADD COLUMN item_type TEXT NOT NULL DEFAULT 'resource';
|
|
ALTER TABLE items ADD COLUMN level INTEGER NOT NULL DEFAULT 1;
|
|
ALTER TABLE items ADD COLUMN price INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN max_stack INTEGER NOT NULL DEFAULT 1;
|
|
ALTER TABLE items ADD COLUMN skill TEXT NOT NULL DEFAULT 'none';
|
|
ALTER TABLE items ADD COLUMN tool TEXT NOT NULL DEFAULT 'none';
|
|
ALTER TABLE items ADD COLUMN description TEXT NOT NULL DEFAULT '';
|
|
|
|
-- Boolean flags (stored as INTEGER: 0=false, 1=true)
|
|
ALTER TABLE items ADD COLUMN two_handed INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN undroppable INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN undroppable_on_death INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN unequip_destroy INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN generate_icon INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN hide_milestone INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN cannot_craft_exceptional INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN storage_all_items INTEGER NOT NULL DEFAULT 0;
|
|
|
|
-- IDs for relationships
|
|
ALTER TABLE items ADD COLUMN ability_id INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN special_ability INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN learn_ability_id INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN book_id INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN swap_item INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE items ADD COLUMN storage_size INTEGER NOT NULL DEFAULT 0;
|
|
```
|
|
|
|
**Use Case:** Direct filtering and sorting
|
|
- "Show all items with level > 50"
|
|
- "Show all weapons"
|
|
- "Show stackable items"
|
|
|
|
---
|
|
|
|
## Phase 2: Create Related Tables
|
|
|
|
### 2.1 Item Categories (many-to-many)
|
|
```sql
|
|
CREATE TABLE item_categories (
|
|
item_id INTEGER NOT NULL,
|
|
category TEXT NOT NULL,
|
|
PRIMARY KEY (item_id, category),
|
|
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_item_categories_category ON item_categories(category);
|
|
```
|
|
|
|
**Use Case:**
|
|
- "Show all bows"
|
|
- "Show all heavy armor"
|
|
|
|
### 2.2 Item Stats (one-to-many)
|
|
```sql
|
|
CREATE TABLE item_stats (
|
|
item_id INTEGER NOT NULL,
|
|
stat_type TEXT NOT NULL,
|
|
value REAL NOT NULL,
|
|
PRIMARY KEY (item_id, stat_type),
|
|
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_item_stats_stat_type ON item_stats(stat_type);
|
|
CREATE INDEX idx_item_stats_value ON item_stats(value);
|
|
```
|
|
|
|
**Use Case:**
|
|
- "Show all items with Health > 100"
|
|
- "Show all items with DamagePhysical"
|
|
|
|
### 2.3 Crafting Recipes (normalized)
|
|
```sql
|
|
CREATE TABLE crafting_recipes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
product_item_id INTEGER NOT NULL,
|
|
skill TEXT NOT NULL,
|
|
level INTEGER NOT NULL,
|
|
workbench_id INTEGER NOT NULL,
|
|
xp INTEGER NOT NULL DEFAULT 0,
|
|
unlocked_by_default INTEGER NOT NULL DEFAULT 1,
|
|
checks TEXT, -- nullable, for conditional recipes
|
|
FOREIGN KEY (product_item_id) REFERENCES items(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_crafting_recipes_product ON crafting_recipes(product_item_id);
|
|
CREATE INDEX idx_crafting_recipes_skill ON crafting_recipes(skill);
|
|
CREATE INDEX idx_crafting_recipes_level ON crafting_recipes(level);
|
|
|
|
CREATE TABLE crafting_recipe_items (
|
|
recipe_id INTEGER NOT NULL,
|
|
item_id INTEGER NOT NULL,
|
|
amount INTEGER NOT NULL,
|
|
PRIMARY KEY (recipe_id, item_id),
|
|
FOREIGN KEY (recipe_id) REFERENCES crafting_recipes(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (item_id) REFERENCES items(id)
|
|
);
|
|
|
|
CREATE INDEX idx_crafting_recipe_items_item ON crafting_recipe_items(item_id);
|
|
```
|
|
|
|
**Use Case:**
|
|
- "Show all recipes that use Copper Ore"
|
|
- "Show all Blacksmithy recipes"
|
|
- "What can I craft with these items?"
|
|
|
|
### 2.4 Item Storage (for storage_items vec)
|
|
```sql
|
|
CREATE TABLE item_storage_allowed (
|
|
storage_item_id INTEGER NOT NULL,
|
|
allowed_item_id INTEGER NOT NULL,
|
|
PRIMARY KEY (storage_item_id, allowed_item_id),
|
|
FOREIGN KEY (storage_item_id) REFERENCES items(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (allowed_item_id) REFERENCES items(id)
|
|
);
|
|
```
|
|
|
|
**Use Case:**
|
|
- "What items can be stored in this container?"
|
|
|
|
### 2.5 Item XP Boosts
|
|
```sql
|
|
CREATE TABLE item_xp_boosts (
|
|
item_id INTEGER NOT NULL,
|
|
skill_type TEXT NOT NULL,
|
|
multiplier REAL NOT NULL,
|
|
PRIMARY KEY (item_id, skill_type),
|
|
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
### 2.6 Permanent Stat Boosts
|
|
```sql
|
|
CREATE TABLE item_permanent_stat_boosts (
|
|
item_id INTEGER NOT NULL,
|
|
stat_type TEXT NOT NULL,
|
|
amount INTEGER NOT NULL,
|
|
PRIMARY KEY (item_id, stat_type),
|
|
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 3: Keep JSON Data Column
|
|
|
|
The `data` column stays as-is for:
|
|
- Complete item retrieval without joins
|
|
- Fields rarely queried (animations, models, audio, etc.)
|
|
- Custom names/descriptions with checks
|
|
- Future flexibility
|
|
|
|
---
|
|
|
|
## Implementation Steps
|
|
|
|
### Step 1: Create Migration File
|
|
- Create `migrations/2026-01-11-expand-items/up.sql`
|
|
- Create `migrations/2026-01-11-expand-items/down.sql`
|
|
|
|
### Step 2: Update Rust Schema
|
|
- Run `diesel migration generate expand_items`
|
|
- Update `src/schema.rs` (diesel will auto-generate)
|
|
|
|
### Step 3: Update `save_to_db()` in `item_database.rs`
|
|
- Insert item columns
|
|
- Insert related records (categories, stats, recipes, etc.)
|
|
- Use transactions for consistency
|
|
|
|
### Step 4: Update `load_from_db()` in `item_database.rs`
|
|
- Load items with all related data
|
|
- Reconstruct full Item struct from columns + related tables
|
|
|
|
### Step 5: Add Query Helper Methods
|
|
```rust
|
|
// Examples:
|
|
pub fn search_by_name(&self, query: &str) -> Vec<&Item>
|
|
pub fn filter_by_level_range(&self, min: i32, max: i32) -> Vec<&Item>
|
|
pub fn filter_by_stat(&self, stat_type: StatType, min_value: f32) -> Vec<&Item>
|
|
pub fn get_items_using_ingredient(&self, ingredient_id: i32) -> Vec<&Item>
|
|
```
|
|
|
|
---
|
|
|
|
## Benefits of This Approach
|
|
|
|
✅ **Efficient Queries** - No JSON parsing for common filters
|
|
✅ **Flexible** - JSON fallback for complex data
|
|
✅ **Maintainable** - Clear relationships between entities
|
|
✅ **Scalable** - Can add indexes as needed
|
|
✅ **Wiki-Friendly** - Easy joins for "Used In" sections
|
|
|
|
---
|
|
|
|
## Query Examples for Wiki/Map
|
|
|
|
```sql
|
|
-- Find all level 50+ weapons
|
|
SELECT * FROM items WHERE item_type = 'weapon' AND level >= 50;
|
|
|
|
-- Find items that give health bonuses
|
|
SELECT i.* FROM items i
|
|
JOIN item_stats s ON i.id = s.item_id
|
|
WHERE s.stat_type = 'health' AND s.value > 0;
|
|
|
|
-- Find all recipes using Copper Ore (id=33)
|
|
SELECT i.name, r.level, r.skill FROM crafting_recipes r
|
|
JOIN crafting_recipe_items ri ON r.id = ri.recipe_id
|
|
JOIN items i ON r.product_item_id = i.id
|
|
WHERE ri.item_id = 33;
|
|
|
|
-- Find all bows
|
|
SELECT i.* FROM items i
|
|
JOIN item_categories c ON i.id = c.item_id
|
|
WHERE c.category = 'bow';
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
Would you like me to:
|
|
1. ✅ Generate the migration files?
|
|
2. ✅ Update the `save_to_db()` and `load_from_db()` methods?
|
|
3. ✅ Add query helper methods?
|
|
4. ⚠️ Keep it simple and just add Phase 1 columns first?
|