mirror of
https://github.com/myronblair/epic-download
synced 2026-06-30 17:51:00 -05:00
auto-commit for 020628f5-4bfa-4157-a41e-90eec0ddfeec
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
# Epic Travel & Expeditions - MongoDB to MySQL Migration Guide
|
||||
|
||||
## Overview
|
||||
This guide helps you migrate the Epic Travel & Expeditions application from MongoDB to MySQL for cPanel deployment.
|
||||
|
||||
## Key Differences
|
||||
|
||||
### Database Structure
|
||||
- **MongoDB**: Document-based, collections, flexible schema
|
||||
- **MySQL**: Table-based, structured schema, relationships
|
||||
|
||||
### Data Type Mapping
|
||||
| MongoDB | MySQL |
|
||||
|---------|-------|
|
||||
| _id (ObjectId) | id VARCHAR(36) - UUID |
|
||||
| String | VARCHAR or TEXT |
|
||||
| Number | INT, DECIMAL, NUMERIC |
|
||||
| Date | DATETIME |
|
||||
| Array | JSON column |
|
||||
| Object | JSON column |
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### 1. Export Data from MongoDB
|
||||
|
||||
```bash
|
||||
# Export destinations
|
||||
mongoexport --db=test_database --collection=destinations --out=destinations.json
|
||||
|
||||
# Export specials
|
||||
mongoexport --db=test_database --collection=specials --out=specials.json
|
||||
|
||||
# Export admin_users
|
||||
mongoexport --db=test_database --collection=admin_users --out=admin_users.json
|
||||
|
||||
# Export contacts
|
||||
mongoexport --db=test_database --collection=contacts --out=contacts.json
|
||||
|
||||
# Export newsletter_subscribers
|
||||
mongoexport --db=test_database --collection=newsletter_subscribers --out=newsletter.json
|
||||
```
|
||||
|
||||
### 2. Transform Data for MySQL
|
||||
|
||||
MongoDB documents need to be transformed to match MySQL schema:
|
||||
|
||||
**MongoDB Document:**
|
||||
```json
|
||||
{
|
||||
"_id": {"$oid": "507f1f77bcf86cd799439011"},
|
||||
"name": "Paris",
|
||||
"rating": 4.9
|
||||
}
|
||||
```
|
||||
|
||||
**MySQL INSERT:**
|
||||
```sql
|
||||
INSERT INTO destinations (id, name, rating)
|
||||
VALUES ('507f1f77bcf86cd799439011', 'Paris', 4.9);
|
||||
```
|
||||
|
||||
### 3. Code Changes Required
|
||||
|
||||
#### Backend Changes
|
||||
|
||||
**Old (MongoDB with Motor):**
|
||||
```python
|
||||
from motor.motor_asyncio import AsyncIOMotorClient
|
||||
|
||||
client = AsyncIOMotorClient(mongo_url)
|
||||
db = client[os.environ['DB_NAME']]
|
||||
|
||||
# Query
|
||||
destinations = await db.destinations.find().to_list(100)
|
||||
```
|
||||
|
||||
**New (MySQL with SQLAlchemy):**
|
||||
```python
|
||||
from sqlalchemy.orm import Session
|
||||
from database import SessionLocal, Destination
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# Query
|
||||
destinations = db.query(Destination).limit(100).all()
|
||||
```
|
||||
|
||||
#### API Route Changes
|
||||
|
||||
**Old (Async MongoDB):**
|
||||
```python
|
||||
@router.get("/destinations")
|
||||
async def get_destinations():
|
||||
destinations = await db.destinations.find().to_list(100)
|
||||
return destinations
|
||||
```
|
||||
|
||||
**New (Sync MySQL):**
|
||||
```python
|
||||
@router.get("/destinations")
|
||||
def get_destinations(db: Session = Depends(get_db)):
|
||||
destinations = db.query(Destination).limit(100).all()
|
||||
return destinations
|
||||
```
|
||||
|
||||
### 4. Environment Variables
|
||||
|
||||
**Old (.env for MongoDB):**
|
||||
```env
|
||||
MONGO_URL=mongodb://localhost:27017
|
||||
DB_NAME=test_database
|
||||
```
|
||||
|
||||
**New (.env for MySQL):**
|
||||
```env
|
||||
MYSQL_HOST=localhost
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_DATABASE=epic_travel
|
||||
MYSQL_USER=dbuser
|
||||
MYSQL_PASSWORD=password
|
||||
```
|
||||
|
||||
### 5. Dependencies
|
||||
|
||||
**Remove:**
|
||||
```
|
||||
motor==3.3.1
|
||||
pymongo==4.5.0
|
||||
```
|
||||
|
||||
**Add:**
|
||||
```
|
||||
PyMySQL>=1.1.0
|
||||
SQLAlchemy>=2.0.23
|
||||
```
|
||||
|
||||
## Automated Migration Script
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Migrate data from MongoDB to MySQL
|
||||
"""
|
||||
from pymongo import MongoClient
|
||||
from sqlalchemy.orm import Session
|
||||
from database import engine, SessionLocal, Destination, Special
|
||||
import uuid
|
||||
|
||||
def migrate_destinations():
|
||||
# Connect to MongoDB
|
||||
mongo_client = MongoClient('mongodb://localhost:27017')
|
||||
mongo_db = mongo_client['test_database']
|
||||
|
||||
# Connect to MySQL
|
||||
mysql_db = SessionLocal()
|
||||
|
||||
try:
|
||||
# Get all destinations from MongoDB
|
||||
mongo_destinations = mongo_db.destinations.find()
|
||||
|
||||
for doc in mongo_destinations:
|
||||
# Transform MongoDB document to SQLAlchemy model
|
||||
destination = Destination(
|
||||
id=str(doc.get('id', uuid.uuid4())),
|
||||
name=doc['name'],
|
||||
location=doc['location'],
|
||||
description=doc['description'],
|
||||
image=doc['image'],
|
||||
category=doc['category'],
|
||||
rating=float(doc['rating']),
|
||||
price=float(doc['price']),
|
||||
currency=doc.get('currency', 'USD'),
|
||||
created_at=doc.get('created_at')
|
||||
)
|
||||
mysql_db.add(destination)
|
||||
|
||||
mysql_db.commit()
|
||||
print(f"Migrated {mongo_destinations.count()} destinations")
|
||||
|
||||
except Exception as e:
|
||||
mysql_db.rollback()
|
||||
print(f"Error: {e}")
|
||||
finally:
|
||||
mysql_db.close()
|
||||
mongo_client.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
migrate_destinations()
|
||||
```
|
||||
|
||||
## Testing Migration
|
||||
|
||||
### 1. Compare Counts
|
||||
```sql
|
||||
-- MySQL
|
||||
SELECT COUNT(*) FROM destinations;
|
||||
SELECT COUNT(*) FROM specials;
|
||||
SELECT COUNT(*) FROM admin_users;
|
||||
```
|
||||
|
||||
```javascript
|
||||
// MongoDB
|
||||
db.destinations.count()
|
||||
db.specials.count()
|
||||
db.admin_users.count()
|
||||
```
|
||||
|
||||
### 2. Sample Data Verification
|
||||
```sql
|
||||
-- Check a specific destination
|
||||
SELECT * FROM destinations WHERE name = 'Paris';
|
||||
```
|
||||
|
||||
### 3. Test Relationships
|
||||
```sql
|
||||
-- Check specials with destinations
|
||||
SELECT d.name, s.discount, s.end_date
|
||||
FROM destinations d
|
||||
JOIN specials s ON d.id = s.destination_id;
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Indexing
|
||||
MySQL indexes are already defined in schema:
|
||||
- Primary keys on id columns
|
||||
- Indexes on frequently queried columns (name, location, category, email)
|
||||
- Foreign keys for relationships
|
||||
|
||||
### Connection Pooling
|
||||
SQLAlchemy provides built-in connection pooling:
|
||||
```python
|
||||
engine = create_engine(
|
||||
DATABASE_URL,
|
||||
pool_size=10,
|
||||
max_overflow=20,
|
||||
pool_recycle=3600
|
||||
)
|
||||
```
|
||||
|
||||
### Query Optimization
|
||||
- Use LIMIT for pagination
|
||||
- Use indexes for WHERE clauses
|
||||
- Use JOIN instead of multiple queries
|
||||
- Cache frequently accessed data
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If migration fails:
|
||||
1. Keep MongoDB running alongside MySQL initially
|
||||
2. Test thoroughly before switching
|
||||
3. Keep MongoDB backups for 30 days
|
||||
4. Have both versions of code ready
|
||||
|
||||
## Post-Migration Checklist
|
||||
|
||||
- [ ] All collections migrated to tables
|
||||
- [ ] Data counts match between MongoDB and MySQL
|
||||
- [ ] All relationships working correctly
|
||||
- [ ] Authentication still working
|
||||
- [ ] API endpoints returning correct data
|
||||
- [ ] Frontend displaying data correctly
|
||||
- [ ] Image uploads working
|
||||
- [ ] Admin dashboard functional
|
||||
- [ ] Contact forms saving to MySQL
|
||||
- [ ] Newsletter subscriptions working
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: Date format differences
|
||||
**Solution:** Convert MongoDB ISODate to MySQL DATETIME
|
||||
```python
|
||||
created_at = datetime.fromisoformat(doc['created_at'])
|
||||
```
|
||||
|
||||
### Issue: JSON array in specials.highlights
|
||||
**Solution:** MySQL JSON column handles this automatically
|
||||
```python
|
||||
highlights = json.dumps(['item1', 'item2']) # Store as JSON string
|
||||
highlights = json.loads(row.highlights) # Retrieve and parse
|
||||
```
|
||||
|
||||
### Issue: UUID vs ObjectId
|
||||
**Solution:** Use UUID strings in MySQL
|
||||
```python
|
||||
import uuid
|
||||
id = str(uuid.uuid4())
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For migration assistance:
|
||||
- Check logs for specific errors
|
||||
- Verify MySQL credentials
|
||||
- Test database connection independently
|
||||
- Review SQLAlchemy documentation
|
||||
- Contact: advisor@epictravelexpeditions.com
|
||||
Reference in New Issue
Block a user