- 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>
498 lines
11 KiB
Markdown
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`. |