- 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>
11 KiB
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:
# CouchDB Configuration
COUCHDB_URL=http://localhost:5984
COUCHDB_DB_NAME=adopt-a-street
Connection Initialization
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 authenticationusers-by-points: Leaderboard queriesstreets-by-location: Geospatial street searchesstreets-by-status: Filter streets by adoption statusposts-by-date: Social feed orderingevents-by-date-status: Event managementreports-by-status: Report workflow management
3. Generic CRUD Operations
// 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
// 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
// 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
// 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
// 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
// Complete a task
const completedTask = await couchdbService.completeTask('user_123', 'task_789');
Social Features
// 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
// Join event
const updatedEvent = await couchdbService.joinEvent('user_123', 'event_789');
Leaderboard & Activity
// 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
// 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
{
_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
{
_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
# From the backend directory
node scripts/migrate-to-couchdb.js
Migration Process
- Phase 1: Export and transform all MongoDB documents to CouchDB format
- Phase 2: Resolve relationships and populate embedded data
- 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:
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
- Connection errors: Service will retry and provide clear error messages
- Document conflicts: Use
resolveConflict()method for handling - Validation errors: Use
validateDocument()before operations - 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
# 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
typefield 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
// 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:
// 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:
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
- Connection refused: Check CouchDB server status
- Database not found: Service creates automatically
- Index not found: Service creates design documents on init
- Document conflicts: Use conflict resolution methods
Debug Mode
Enable debug logging by setting environment variable:
DEBUG=couchdb* node server.js
Future Enhancements
Planned Features
- Change feed integration: Real-time updates
- Replication support: Multi-instance deployment
- Advanced analytics: Complex aggregations
- Caching layer: Redis integration
- Connection pooling: High-performance scaling
Extensibility
The service is designed to be easily extended:
// 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.