Files

163 lines
5.3 KiB
Markdown

# DBSyncer - MongoDB to PostgreSQL Data Synchronization
A comprehensive data synchronization tool that:
1. Downloads Television data from the TVMaze API and stores it in MongoDB
2. Automatically generates PostgreSQL table schemas from MongoDB document structures
3. Efficiently inserts/updates MongoDB documents into PostgreSQL with intelligent type conversion
## Features
### Schema Generation (`db/schema_generator.py`)
- **Automatic Schema Inference**: Analyzes MongoDB documents to infer PostgreSQL types
- **Type Mapping**: Intelligent conversion between MongoDB and PostgreSQL types:
- Objects/Arrays → JSONB
- Dates → TIMESTAMP
- Numbers → BIGINT / DOUBLE PRECISION
- Strings → TEXT
### Document Insertion
- **Batch Processing**: Efficiently inserts documents in configurable batches (default: 1000)
- **Type Conversion**: Automatic type conversion for:
- Epoch timestamps ↔ ISO datetime strings
- JSON strings ↔ Python dict/list
- ObjectId → Text
- **UPSERT Support**: Uses PostgreSQL `ON CONFLICT ... DO UPDATE SET` for automatic upserts
- **Error Handling**: Detailed logging with per-row error reporting
## Quick Start
### Prerequisites
```bash
pip install -r requirements.txt
```
### Configuration
Edit `settings/tvsync_settings.cfg`:
```ini
[dbsettings]
hostname = localhost
pgsqlport = 5432
pgsqlusername = postgres
pgsqlpassword = your_password
dbname = media_dbsync
mghostname = localhost
mgport = 27017
mgdbname = media
[database]
dbtype = pgsql
updateschema = updates
```
### Main Scripts
#### `update_mongodb.py`
Fetches TV data from TVMaze API and stores in MongoDB:
```bash
python3 update_mongodb.py
```
#### `mongodb2postgres.py`
Generates PostgreSQL schemas from MongoDB collections and inserts documents:
```bash
python3 mongodb2postgres.py
```
This script will:
1. Analyze each MongoDB collection (series, episodes, actors, characters, crew)
2. Automatically create corresponding PostgreSQL tables in the `updates` schema
3. Insert/upsert all documents with proper type conversions
4. Report progress and any errors
## Usage Examples
### Generate Table Schema from MongoDB Collection
```python
from db.schema_generator import MongoToPostgresSchemaGenerator
generator = MongoToPostgresSchemaGenerator(sample_size=100)
schema = generator.analyze_collection(mongo_db, 'series')
# View the SQL
sql = generator.generate_create_table_sql('seriesdata', schema_name='dbo', pk_field='id')
print(sql)
# Create the table
generator.create_table_in_postgres(engine, 'seriesdata', schema_name='dbo', pk_field='id')
```
### Insert Documents with Type Conversion
```python
from db.schema_generator import MongoDocumentInserter
inserter = MongoDocumentInserter(batch_size=1000)
# From a collection
count = inserter.insert_from_collection(
engine=engine,
table_name='seriesdata',
collection=mongo_db['series'],
schema_name='dbo',
on_conflict="ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, updated=EXCLUDED.updated",
)
print(f"Inserted {count} documents")
```
## Architecture
### Database Classes (`db/functions.py`)
- `settype`: Manages database engine creation and API configuration
- `dbmongo`: Handles MongoDB connections and operations
- `Dbexec`: Executes raw SQL queries and batch updates
### Schema Generator (`db/schema_generator.py`)
- `MongoToPostgresSchemaGenerator`: Infers and creates table schemas
- `MongoDocumentInserter`: Handles document insertion with type conversion
### API Integration (`api/`)
- TVMaze API client for fetching show, episode, cast, and crew data
- TheTVDB API client (optional)
## Performance Optimizations
- **Batch Inserts**: Uses SQLAlchemy's parameterized queries for efficient batch operations
- **Transactional Inserts**: Groups inserts in transactions to reduce database overhead
- **Column Type Awareness**: Converts data to match target column types, avoiding casting overhead
- **Connection Pooling**: Reuses database connections (pool_size=20, max_overflow=20)
- **UPSERT Operations**: Uses PostgreSQL's `ON CONFLICT` for atomic insert-or-update
## Troubleshooting
### MetaData Schema Argument Error
If you see `sqlalchemy.exc.ArgumentError: Could not parse SQLAlchemy URL`, ensure:
- Use `MetaData(schema="updates")` instead of `MetaData("updates")`
- Pass full connection strings to `create_engine()`, not schema names
### Type Conversion Errors
The inserter automatically handles:
- Datetime strings → epoch timestamps (for BIGINT columns)
- Epoch ints → datetime objects (for TIMESTAMP columns)
- JSON strings → Python dicts (for JSONB columns)
If type errors persist, check that:
1. Table schema matches MongoDB document fields
2. Column types are correctly inferred from sample documents
3. Use appropriate `on_conflict` clauses for upserts
### Column Name Case Sensitivity
PostgreSQL stores unquoted identifiers as lowercase. The inserter automatically:
- Lowercases column names in `ON CONFLICT` clauses
- Uses case-insensitive matching when mapping MongoDB fields to PostgreSQL columns
## Contributing
When modifying:
- **Schema inference**: Update `MongoToPostgresSchemaGenerator.TYPE_MAPPING` for new type support
- **Type conversion**: Extend `MongoDocumentInserter.convert_value()` for custom conversions
- **Database operations**: Use parameterized queries via `text()` to prevent SQL injection
- **Error handling**: Add specific exception types to improve debugging
## License
See LICENSE file for details.