Files
adopt-a-street/backend/COUCHDB_SERVICE_GUIDE.md
William Valentin 2961107136 feat: implement comprehensive CouchDB service and migration utilities
- Add production-ready CouchDB service with connection management
- Implement design documents with views and Mango indexes
- Create CRUD operations with proper error handling
- Add specialized helper methods for all document types
- Include batch operations and conflict resolution
- Create comprehensive migration script from MongoDB to CouchDB
- Add test suite with graceful handling when CouchDB unavailable
- Include detailed documentation and usage guide
- Update environment configuration for CouchDB support
- Follow existing code patterns and conventions

🤖 Generated with [AI Assistant]

Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
2025-11-01 13:05:18 -07:00

498 lines
11 KiB
Markdown

# CouchDB Service Guide
This guide provides comprehensive documentation for the CouchDB service implementation in the Adopt-a-Street application.
## Overview
The `CouchDBService` class provides a production-ready interface for interacting with CouchDB, replacing MongoDB as the primary database. It includes connection management, CRUD operations, query helpers, and migration utilities.
## Configuration
### Environment Variables
Add these to your `.env` file:
```bash
# CouchDB Configuration
COUCHDB_URL=http://localhost:5984
COUCHDB_DB_NAME=adopt-a-street
```
### Connection Initialization
```javascript
const couchdbService = require('./services/couchdbService');
// Initialize the service (call once at application startup)
await couchdbService.initialize();
// Check if connected
if (couchdbService.isReady()) {
console.log('CouchDB is ready');
}
```
## Core Features
### 1. Connection Management
- **Automatic connection**: Establishes connection on first use
- **Database creation**: Creates database if it doesn't exist
- **Health checks**: Validates connection status
- **Graceful shutdown**: Properly closes connections
### 2. Design Documents & Indexes
The service automatically creates design documents with:
- **Views**: For common query patterns
- **Mango Indexes**: For efficient document queries
- **Geospatial indexes**: For location-based queries
#### Available Indexes
- `user-by-email`: Fast user authentication
- `users-by-points`: Leaderboard queries
- `streets-by-location`: Geospatial street searches
- `streets-by-status`: Filter streets by adoption status
- `posts-by-date`: Social feed ordering
- `events-by-date-status`: Event management
- `reports-by-status`: Report workflow management
### 3. Generic CRUD Operations
```javascript
// Create document
const doc = {
_id: 'user_123',
type: 'user',
name: 'John Doe',
email: 'john@example.com'
};
const created = await couchdbService.createDocument(doc);
// Get document
const retrieved = await couchdbService.getDocument('user_123');
// Update document
created.name = 'Jane Doe';
const updated = await couchdbService.updateDocument(created);
// Delete document
await couchdbService.deleteDocument('user_123', updated._rev);
```
### 4. Query Operations
```javascript
// Find with Mango query
const users = await couchdbService.find({
selector: {
type: 'user',
points: { $gt: 100 }
},
sort: [{ points: 'desc' }],
limit: 10
});
// Find single document
const user = await couchdbService.findOne({
type: 'user',
email: 'john@example.com'
});
// Find by type
const streets = await couchdbService.findByType('street', {
status: 'available'
});
// Use views
const topUsers = await couchdbService.view('users', 'by-points', {
limit: 10,
descending: true
});
```
### 5. Batch Operations
```javascript
// Bulk create/update
const docs = [
{ _id: 'doc1', type: 'test', name: 'Test 1' },
{ _id: 'doc2', type: 'test', name: 'Test 2' }
];
const result = await couchdbService.bulkDocs({ docs });
```
### 6. Specialized Helper Methods
#### User Operations
```javascript
// Find user by email
const user = await couchdbService.findUserByEmail('john@example.com');
// Update user points with transaction
const updatedUser = await couchdbService.updateUserPoints(
'user_123',
50,
'Completed task: Clean up street',
{
entityType: 'Task',
entityId: 'task_456',
entityName: 'Clean up street'
}
);
```
#### Street Operations
```javascript
// Find streets by location (geospatial)
const bounds = [[-74.1, 40.6], [-73.9, 40.8]]; // NYC area
const streets = await couchdbService.findStreetsByLocation(bounds);
// Adopt a street
const adoptedStreet = await couchdbService.adoptStreet('user_123', 'street_456');
```
#### Task Operations
```javascript
// Complete a task
const completedTask = await couchdbService.completeTask('user_123', 'task_789');
```
#### Social Features
```javascript
// Create a post
const post = await couchdbService.createPost('user_123', {
content: 'Great day cleaning up!',
imageUrl: 'https://example.com/image.jpg',
cloudinaryPublicId: 'post_123'
});
// Toggle like on post
const updatedPost = await couchdbService.togglePostLike('user_123', 'post_456');
// Add comment
const comment = await couchdbService.addCommentToPost(
'user_123',
'post_456',
'Great work! 🎉'
);
```
#### Event Management
```javascript
// Join event
const updatedEvent = await couchdbService.joinEvent('user_123', 'event_789');
```
#### Leaderboard & Activity
```javascript
// Get leaderboard
const leaderboard = await couchdbService.getLeaderboard(10);
// Get user activity feed
const activity = await couchdbService.getUserActivity('user_123', 20);
// Get social feed
const feed = await couchdbService.getSocialFeed(20, 0);
```
#### Report Management
```javascript
// Create report
const report = await couchdbService.createReport('user_123', 'street_456', {
issue: 'Broken streetlight',
imageUrl: 'https://example.com/report.jpg',
cloudinaryPublicId: 'report_123'
});
// Resolve report
const resolved = await couchdbService.resolveReport('report_789');
```
## Document Structure
### User Document
```javascript
{
_id: "user_1234567890abcdef",
type: "user",
name: "John Doe",
email: "john@example.com",
password: "hashed_password",
isPremium: false,
points: 150,
profilePicture: "https://cloudinary.com/image.jpg",
cloudinaryPublicId: "abc123",
adoptedStreets: ["street_abc123", "street_def456"],
completedTasks: ["task_123", "task_456"],
posts: ["post_789", "post_012"],
events: ["event_345", "event_678"],
earnedBadges: [
{
badgeId: "badge_123",
name: "Street Hero",
description: "Adopted 5 streets",
icon: "🏆",
rarity: "rare",
earnedAt: "2024-01-15T10:30:00Z",
progress: 100
}
],
stats: {
streetsAdopted: 2,
tasksCompleted: 5,
postsCreated: 3,
eventsParticipated: 2,
badgesEarned: 1
},
createdAt: "2024-01-01T00:00:00Z",
updatedAt: "2024-01-15T10:30:00Z"
}
```
### Street Document
```javascript
{
_id: "street_abc123def456",
type: "street",
name: "Main Street",
location: {
type: "Point",
coordinates: [-74.0060, 40.7128]
},
adoptedBy: {
userId: "user_1234567890abcdef",
name: "John Doe",
profilePicture: "https://cloudinary.com/image.jpg"
},
status: "adopted",
stats: {
tasksCount: 5,
completedTasksCount: 3,
reportsCount: 2,
openReportsCount: 1
},
createdAt: "2024-01-01T00:00:00Z",
updatedAt: "2024-01-15T10:30:00Z"
}
```
## Migration
### Running the Migration
```bash
# From the backend directory
node scripts/migrate-to-couchdb.js
```
### Migration Process
1. **Phase 1**: Export and transform all MongoDB documents to CouchDB format
2. **Phase 2**: Resolve relationships and populate embedded data
3. **Phase 3**: Calculate statistics and counters
### Migration Features
- **ID transformation**: MongoDB ObjectIds → prefixed string IDs
- **Relationship resolution**: Populates embedded user/street data
- **Statistics calculation**: Computes counts and aggregates
- **Error handling**: Continues migration even if some documents fail
- **Progress tracking**: Shows migration status and statistics
## Error Handling
The service provides comprehensive error handling:
```javascript
try {
const doc = await couchdbService.getDocument('nonexistent');
if (!doc) {
console.log('Document not found');
}
} catch (error) {
console.error('Database error:', error.message);
}
```
### Common Error Scenarios
1. **Connection errors**: Service will retry and provide clear error messages
2. **Document conflicts**: Use `resolveConflict()` method for handling
3. **Validation errors**: Use `validateDocument()` before operations
4. **Query errors**: Detailed error messages for invalid queries
## Performance Considerations
### Index Usage
- Always use indexed fields in selectors
- Use appropriate limit/sort combinations
- Consider view queries for complex aggregations
### Batch Operations
- Use `bulkDocs()` for multiple document operations
- Batch size recommendations: 100-500 documents per operation
### Memory Management
- Service maintains single connection instance
- Automatic cleanup on shutdown
- Efficient document streaming for large datasets
## Testing
### Running Tests
```bash
# Run CouchDB service tests
bun test __tests__/services/couchdbService.test.js
```
### Test Coverage
- Connection management
- CRUD operations
- Query functionality
- Helper methods
- Error handling
- Migration utilities
## Best Practices
### 1. Document Design
- Include `type` field in all documents
- Use consistent ID prefixes (`user_`, `street_`, etc.)
- Embed frequently accessed data
- Keep document sizes reasonable (< 1MB)
### 2. Query Optimization
- Use appropriate indexes
- Limit result sets with pagination
- Prefer views for complex queries
- Use selectors efficiently
### 3. Error Handling
- Always wrap database calls in try-catch
- Check for null returns from `getDocument()`
- Handle conflicts gracefully
- Log errors for debugging
### 4. Performance
- Use batch operations for bulk changes
- Implement proper pagination
- Cache frequently accessed data
- Monitor query performance
## Integration with Existing Code
### Replacing MongoDB Operations
```javascript
// Before (MongoDB)
const user = await User.findOne({ email: 'john@example.com' });
// After (CouchDB)
const user = await couchdbService.findUserByEmail('john@example.com');
```
### Updating Routes
Most route handlers can be updated by replacing Mongoose calls with CouchDB service methods:
```javascript
// Example route update
router.get('/users/:id', async (req, res) => {
try {
const user = await couchdbService.getDocument(`user_${req.params.id}`);
if (!user) {
return res.status(404).json({ msg: 'User not found' });
}
res.json(user);
} catch (error) {
console.error(error.message);
res.status(500).send('Server error');
}
});
```
## Monitoring & Maintenance
### Health Checks
The service provides connection status monitoring:
```javascript
if (couchdbService.isReady()) {
// Database is available
} else {
// Handle database unavailability
}
```
### Performance Monitoring
- Monitor query response times
- Track document sizes
- Watch for connection pool issues
- Monitor index usage
## Troubleshooting
### Common Issues
1. **Connection refused**: Check CouchDB server status
2. **Database not found**: Service creates automatically
3. **Index not found**: Service creates design documents on init
4. **Document conflicts**: Use conflict resolution methods
### Debug Mode
Enable debug logging by setting environment variable:
```bash
DEBUG=couchdb* node server.js
```
## Future Enhancements
### Planned Features
1. **Change feed integration**: Real-time updates
2. **Replication support**: Multi-instance deployment
3. **Advanced analytics**: Complex aggregations
4. **Caching layer**: Redis integration
5. **Connection pooling**: High-performance scaling
### Extensibility
The service is designed to be easily extended:
```javascript
// Add custom helper methods
couchdbService.customMethod = async function(params) {
// Custom implementation
};
```
## Conclusion
The CouchDB service provides a robust, production-ready replacement for MongoDB in the Adopt-a-Street application. It offers comprehensive functionality, proper error handling, and migration tools to ensure a smooth transition.
For questions or issues, refer to the test files and implementation details in `backend/services/couchdbService.js`.