# CouchDB Query Examples This document demonstrates how the CouchDB design handles common query patterns for the Adopt-a-Street application. ## 1. User Authentication ### MongoDB Query ```javascript const user = await User.findOne({ email: "john@example.com" }); ``` ### CouchDB Equivalent ```javascript // Using Mango query const user = await couchdbService.findUserByEmail("john@example.com"); // Raw Mango query { "selector": { "type": "user", "email": "john@example.com" }, "limit": 1 } ``` **Performance**: Single document lookup with indexed email field. ## 2. Geospatial Street Search ### MongoDB Query ```javascript const streets = await Street.find({ location: { $geoWithin: { $box: [[-74.1, 40.6], [-73.9, 40.8]] } }, status: "available" }); ``` ### CouchDB Equivalent ```javascript // Using service method const streets = await couchdbService.findStreetsByLocation([[-74.1, 40.6], [-73.9, 40.8]]); // Raw Mango query { "selector": { "type": "street", "status": "available", "location": { "$geoWithin": { "$box": [[-74.1, 40.6], [-73.9, 40.8]] } } } } ``` **Performance**: Geospatial index on location field, filtered by status. ## 3. User's Activity Feed ### MongoDB Query (Multiple Queries) ```javascript const posts = await Post.find({ user: userId }).sort({ createdAt: -1 }); const tasks = await Task.find({ completedBy: userId }).sort({ updatedAt: -1 }); const events = await Event.find({ participants: userId }).sort({ date: -1 }); // Combine and sort results const activity = [...posts, ...tasks, ...events].sort((a, b) => b.createdAt - a.createdAt); ``` ### CouchDB Equivalent (Single Query) ```javascript // Using service method const activity = await couchdbService.getUserActivity(userId, 50); // Raw Mango query { "selector": { "$or": [ { "type": "post", "user.userId": "user_1234567890abcdef" }, { "type": "task", "completedBy.userId": "user_1234567890abcdef" }, { "type": "event", "participants": { "$elemMatch": { "userId": "user_1234567890abcdef" } } } ] }, "sort": [{"createdAt": "desc"}], "limit": 50 } ``` **Performance**: Single query with compound OR selector, sorted by creation date. ## 4. Social Feed with User Data ### MongoDB Query (Population Required) ```javascript const posts = await Post.find({}) .populate('user', 'name profilePicture') .sort({ createdAt: -1 }) .limit(20); // For comments, separate query needed const comments = await Comment.find({ post: { $in: posts.map(p => p._id) } }) .populate('user', 'name profilePicture') .sort({ createdAt: 1 }); ``` ### CouchDB Equivalent (No Population Needed) ```javascript // Get posts with embedded user data const posts = await couchdbService.getSocialFeed(20); // Get comments for posts const postIds = posts.map(p => p._id); const comments = await couchdbService.getPostComments(postIds[0]); // Example for one post // Raw Mango query for posts { "selector": { "type": "post" }, "sort": [{"createdAt": "desc"}], "limit": 20 } // Raw Mango query for comments { "selector": { "type": "comment", "post.postId": {"$in": ["post_123", "post_456"]} }, "sort": [{"createdAt": "asc"}] } ``` **Performance**: User data embedded in posts, no additional lookups needed. ## 5. Leaderboard ### MongoDB Query ```javascript const users = await User.find({ points: { $gt: 0 } }) .select('name points profilePicture') .sort({ points: -1 }) .limit(10); ``` ### CouchDB Equivalent ```javascript // Using service method const leaderboard = await couchdbService.getLeaderboard(10); // Raw Mango query { "selector": { "type": "user", "points": {"$gt": 0} }, "sort": [{"points": "desc"}], "limit": 10, "fields": ["_id", "name", "points", "profilePicture", "stats"] } ``` **Performance**: Indexed query on points field with descending sort. ## 6. Street Details with Related Data ### MongoDB Query (Multiple Queries) ```javascript const street = await Street.findById(streetId).populate('adoptedBy', 'name profilePicture'); const tasks = await Task.find({ street: streetId }); const reports = await Report.find({ street: streetId }); // Calculate stats manually const stats = { tasksCount: tasks.length, completedTasksCount: tasks.filter(t => t.status === 'completed').length, reportsCount: reports.length, openReportsCount: reports.filter(r => r.status === 'open').length }; ``` ### CouchDB Equivalent (Single Document) ```javascript // Single document contains all needed data const street = await couchdbService.getById(streetId); // Raw Mango query { "selector": { "_id": "street_abc123def456" } } // Result includes embedded stats: { "_id": "street_abc123def456", "type": "street", "name": "Main Street", "adoptedBy": { "userId": "user_123", "name": "John Doe", "profilePicture": "https://cloudinary.com/image.jpg" }, "stats": { "tasksCount": 5, "completedTasksCount": 3, "reportsCount": 2, "openReportsCount": 1 } } ``` **Performance**: Single document lookup with pre-calculated stats. ## 7. Event Management ### MongoDB Query ```javascript const events = await Event.find({ date: { $gte: new Date() }, status: "upcoming" }).populate('participants', 'name profilePicture'); ``` ### CouchDB Equivalent ```javascript // Using service method const events = await couchdbService.findByType('event', { date: { $gte: new Date().toISOString() }, status: 'upcoming' }); // Raw Mango query { "selector": { "type": "event", "date": {"$gte": "2024-01-15T00:00:00Z"}, "status": "upcoming" }, "sort": [{"date": "asc"}] } // Result includes embedded participant data: { "_id": "event_123", "type": "event", "title": "Community Cleanup", "participants": [ { "userId": "user_123", "name": "John Doe", "profilePicture": "https://cloudinary.com/image.jpg", "joinedAt": "2024-01-10T10:00:00Z" } ], "participantsCount": 1 } ``` **Performance**: Participant data embedded, no population needed. ## 8. Badge System ### MongoDB Query (Complex Join) ```javascript const user = await User.findById(userId).populate({ path: 'earnedBadges', populate: { path: 'badge', model: 'Badge' } }); ``` ### CouchDB Equivalent (Embedded Data) ```javascript // Single user document contains badge data const user = await couchdbService.findUserById(userId); // Raw Mango query { "selector": { "_id": "user_1234567890abcdef" } } // Result includes embedded badges: { "_id": "user_1234567890abcdef", "type": "user", "name": "John Doe", "earnedBadges": [ { "badgeId": "badge_123", "name": "Street Hero", "description": "Adopted 5 streets", "icon": "🏆", "rarity": "rare", "earnedAt": "2024-01-15T10:30:00Z", "progress": 100 } ], "stats": { "badgesEarned": 1 } } ``` **Performance**: Badge data embedded in user document, no joins required. ## 9. Point Transaction History ### MongoDB Query ```javascript const transactions = await PointTransaction.find({ user: userId }) .sort({ createdAt: -1 }) .limit(50); ``` ### CouchDB Equivalent ```javascript // Using service method const transactions = await couchdbService.find({ type: 'point_transaction', 'user.userId': userId }, { sort: [{ createdAt: 'desc' }], limit: 50 }); // Raw Mango query { "selector": { "type": "point_transaction", "user.userId": "user_1234567890abcdef" }, "sort": [{"createdAt": "desc"}], "limit": 50 } // Result includes embedded user data: { "_id": "transaction_123", "type": "point_transaction", "user": { "userId": "user_123", "name": "John Doe" }, "amount": 10, "type": "task_completion", "description": "Completed task: Clean up litter", "relatedEntity": { "entityType": "Task", "entityId": "task_456", "entityName": "Clean up litter" }, "balanceAfter": 150 } ``` **Performance**: Indexed query on user and creation date. ## 10. Real-time Updates with Changes Feed ### MongoDB (Change Streams) ```javascript const changeStream = User.watch(); changeStream.on('change', (change) => { // Handle user changes }); ``` ### CouchDB (Changes Feed) ```javascript // Listen to changes feed const changes = couchdbService.db.changes({ since: 'now', live: true, include_docs: true }); changes.on('change', (change) => { const doc = change.doc; // Handle different document types switch (doc.type) { case 'user': // Handle user updates break; case 'post': // Handle new posts break; case 'event': // Handle event updates break; } }); // Filter by document type const userChanges = couchdbService.db.changes({ since: 'now', live: true, include_docs: true, filter: '_design/app', selector: { type: 'user' } }); ``` **Performance**: Native real-time updates with filtering capabilities. ## Performance Comparison Summary | Query Pattern | MongoDB | CouchDB | Performance Impact | |---------------|---------|---------|-------------------| | User Auth | 1 query + index | 1 query + index | Similar | | Social Feed | 1 query + populate | 1 query (embedded) | CouchDB faster | | User Activity | 3 queries + combine | 1 query (OR) | CouchDB faster | | Leaderboard | 1 query + index | 1 query + index | Similar | | Street Details | 4 queries + calc | 1 query (embedded) | CouchDB much faster | | Event Management | 1 query + populate | 1 query (embedded) | CouchDB faster | | Badge System | Complex populate | Embedded data | CouchDB much faster | | Real-time Updates | Change Streams | Changes Feed | CouchDB more flexible | ## Key Benefits of CouchDB Design 1. **Reduced Query Complexity**: Most common queries become single-document lookups 2. **Better Read Performance**: Embedded data eliminates JOIN operations 3. **Simplified Application Logic**: No need for complex population strategies 4. **Improved Offline Support**: Rich documents enable better offline functionality 5. **Real-time Capabilities**: Native changes feed with flexible filtering 6. **Scalability**: Denormalized design scales well with read-heavy workloads ## Trade-offs and Mitigations 1. **Data Duplication**: User data appears in multiple documents - **Mitigation**: Use changes feed to propagate updates 2. **Update Complexity**: Changes require updating multiple documents - **Mitigation**: Batch updates and background reconciliation jobs 3. **Storage Overhead**: Larger documents due to embedded data - **Mitigation**: Selective embedding based on access patterns 4. **Consistency**: Eventual consistency for related data - **Mitigation**: Application-level consistency checks and reconciliation This CouchDB design prioritizes read performance and user experience, which aligns perfectly with the social community nature of the Adopt-a-Street application where most operations are reads rather than writes.