# 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`.