Ejemplos CouchDB

Ejemplos de base de datos NoSQL Apache CouchDB incluyendo operaciones de documentos, vistas y replicación

💻 Operaciones Básicas CouchDB javascript

🟢 simple ⭐⭐

Operaciones CRUD fundamentales con documentos y bases de datos CouchDB

⏱️ 20 min 🏷️ couchdb, nosql, document database, nodejs
Prerequisites: Basic JavaScript/Node.js, NoSQL concepts
// CouchDB Basic Operations
// JavaScript (Node.js) - Using nano library

const nano = require('nano')('http://localhost:5984');

// 1. Database Operations
class CouchDBManager {
    constructor(dbName) {
        this.dbName = dbName;
        this.db = nano.db.use(dbName);
    }

    // Create database
    async createDatabase() {
        try {
            await nano.db.create(this.dbName);
            console.log(`✅ Database '${this.dbName}' created successfully`);
            return true;
        } catch (error) {
            if (error.statusCode === 412) {
                console.log(`ℹ️ Database '${this.dbName}' already exists`);
            } else {
                console.error('❌ Error creating database:', error.message);
            }
            return false;
        }
    }

    // Delete database
    async deleteDatabase() {
        try {
            await nano.db.destroy(this.dbName);
            console.log(`✅ Database '${this.dbName}' deleted successfully`);
            return true;
        } catch (error) {
            console.error('❌ Error deleting database:', error.message);
            return false;
        }
    }

    // List all databases
    async listDatabases() {
        try {
            const databases = await nano.db.list();
            console.log('📊 Available databases:', databases);
            return databases;
        } catch (error) {
            console.error('❌ Error listing databases:', error.message);
            return [];
        }
    }

    // 2. Document Operations (CRUD)

    // Create document
    async createDocument(doc) {
        try {
            const response = await this.db.insert(doc);
            console.log(`✅ Document created with ID: ${response.id}, Rev: ${response.rev}`);
            return response;
        } catch (error) {
            console.error('❌ Error creating document:', error.message);
            throw error;
        }
    }

    // Read document by ID
    async getDocument(docId) {
        try {
            const doc = await this.db.get(docId);
            console.log(`📄 Retrieved document: ${JSON.stringify(doc, null, 2)}`);
            return doc;
        } catch (error) {
            if (error.statusCode === 404) {
                console.log(`⚠️ Document with ID '${docId}' not found`);
            } else {
                console.error('❌ Error getting document:', error.message);
            }
            return null;
        }
    }

    // Update document
    async updateDocument(docId, updatedDoc) {
        try {
            // First get the current document to get the _rev
            const currentDoc = await this.getDocument(docId);
            if (!currentDoc) {
                throw new Error('Document not found');
            }

            // Update the document with current _rev
            const docToUpdate = {
                ...updatedDoc,
                _id: docId,
                _rev: currentDoc._rev
            };

            const response = await this.db.insert(docToUpdate);
            console.log(`✅ Document updated with new Rev: ${response.rev}`);
            return response;
        } catch (error) {
            console.error('❌ Error updating document:', error.message);
            throw error;
        }
    }

    // Delete document
    async deleteDocument(docId) {
        try {
            const doc = await this.getDocument(docId);
            if (!doc) {
                throw new Error('Document not found');
            }

            const response = await this.db.destroy(docId, doc._rev);
            console.log(`✅ Document '${docId}' deleted successfully`);
            return response;
        } catch (error) {
            console.error('❌ Error deleting document:', error.message);
            throw error;
        }
    }

    // 3. Bulk Operations

    // Bulk create documents
    async bulkCreateDocuments(docs) {
        try {
            const response = await this.db.bulk({ docs });
            console.log(`✅ Bulk operation completed. ${response.filter(r => !r.error).length} documents created`);
            return response;
        } catch (error) {
            console.error('❌ Error in bulk create:', error.message);
            throw error;
        }
    }

    // 4. Query Operations

    // Get all documents
    async getAllDocuments() {
        try {
            const response = await this.db.list({ include_docs: true });
            console.log(`📋 Found ${response.total_rows} documents`);
            return response.rows.map(row => row.doc);
        } catch (error) {
            console.error('❌ Error getting all documents:', error.message);
            return [];
        }
    }

    // Query documents with Mango query
    async queryDocuments(selector, options = {}) {
        try {
            const response = await this.db.find({
                selector,
                ...options
            });
            console.log(`🔍 Query returned ${response.docs.length} documents`);
            return response.docs;
        } catch (error) {
            console.error('❌ Error querying documents:', error.message);
            return [];
        }
    }

    // 5. Document Design and Views
    async createDesignDocument(dDoc) {
        try {
            const response = await this.db.insert(dDoc);
            console.log(`✅ Design document created: ${response.id}`);
            return response;
        } catch (error) {
            console.error('❌ Error creating design document:', error.message);
            throw error;
        }
    }

    // Query view
    async queryView(dDocName, viewName, options = {}) {
        try {
            const response = await this.db.view(dDocName, viewName, options);
            console.log(`📊 View '${dDocName}/${viewName}' returned ${response.rows.length} results`);
            return response;
        } catch (error) {
            console.error('❌ Error querying view:', error.message);
            return null;
        }
    }
}

// Usage Examples
async function demonstrateCouchDB() {
    const dbManager = new CouchDBManager('example_db');

    // Create database
    await dbManager.createDatabase();

    // Create sample documents
    const userDoc = {
        type: 'user',
        name: 'John Doe',
        email: '[email protected]',
        age: 30,
        created_at: new Date().toISOString()
    };

    const productDoc = {
        type: 'product',
        name: 'Laptop',
        price: 999.99,
        category: 'Electronics',
        in_stock: true,
        created_at: new Date().toISOString()
    };

    // Create documents
    const userResult = await dbManager.createDocument(userDoc);
    const productResult = await dbManager.createDocument(productDoc);

    // Read documents
    await dbManager.getDocument(userResult.id);

    // Update document
    await dbManager.updateDocument(userResult.id, {
        ...userDoc,
        age: 31,
        last_updated: new Date().toISOString()
    });

    // Query with Mango
    const electronics = await dbManager.queryDocuments({
        type: 'product',
        category: 'Electronics'
    });

    console.log('Electronics products:', electronics);

    // Create design document with views
    const designDoc = {
        _id: '_design/products',
        views: {
            by_category: {
                map: function(doc) {
                    if (doc.type === 'product' && doc.category) {
                        emit(doc.category, doc.price);
                    }
                }.toString()
            },
            in_stock: {
                map: function(doc) {
                    if (doc.type === 'product' && doc.in_stock === true) {
                        emit(doc.name, null);
                    }
                }.toString()
            }
        }
    };

    await dbManager.createDesignDocument(designDoc);

    // Query views
    const categoryView = await dbManager.queryView('products', 'by_category', {
        group: true,
        reduce: true
    });

    console.log('Products by category:', categoryView);
}

// 6. Attachment Management
class CouchAttachmentManager extends CouchDBManager {
    // Add attachment to document
    async addAttachment(docId, attachmentName, attachmentData, contentType) {
        try {
            const doc = await this.getDocument(docId);
            const response = await this.db.attachment.insert(
                docId,
                attachmentName,
                attachmentData,
                contentType,
                doc._rev
            );
            console.log(`✅ Attachment '${attachmentName}' added to document '${docId}'`);
            return response;
        } catch (error) {
            console.error('❌ Error adding attachment:', error.message);
            throw error;
        }
    }

    // Get attachment
    async getAttachment(docId, attachmentName) {
        try {
            const response = await this.db.attachment.get(docId, attachmentName);
            console.log(`📎 Retrieved attachment '${attachmentName}' from document '${docId}'`);
            return response;
        } catch (error) {
            console.error('❌ Error getting attachment:', error.message);
            return null;
        }
    }

    // Delete attachment
    async deleteAttachment(docId, attachmentName) {
        try {
            const doc = await this.getDocument(docId);
            const response = await this.db.attachment.destroy(docId, attachmentName, doc._rev);
            console.log(`🗑️ Attachment '${attachmentName}' deleted from document '${docId}'`);
            return response;
        } catch (error) {
            console.error('❌ Error deleting attachment:', error.message);
            throw error;
        }
    }
}

// 7. Change Management
class CouchChangeTracker extends CouchDBManager {
    // Track changes since last update
    async trackChanges(since = 'now', options = {}) {
        try {
            const feed = this.db.follow({
                since,
                include_docs: true,
                ...options
            });

            feed.on('change', (change) => {
                console.log('📝 Change detected:', {
                    id: change.id,
                    seq: change.seq,
                    deleted: change.deleted,
                    changes: change.changes
                });

                if (change.doc) {
                    console.log('📄 Changed document:', change.doc);
                }
            });

            feed.on('error', (error) => {
                console.error('❌ Change tracking error:', error);
            });

            feed.follow();
            console.log('👂 Started tracking changes...');
            return feed;
        } catch (error) {
            console.error('❌ Error setting up change tracking:', error.message);
            return null;
        }
    }

    // Get changes once
    async getChanges(since = 0, options = {}) {
        try {
            const changes = await this.db.changes({
                since,
                include_docs: true,
                ...options
            });

            console.log(`📋 Found ${changes.results.length} changes`);
            return changes;
        } catch (error) {
            console.error('❌ Error getting changes:', error.message);
            return null;
        }
    }
}

// 8. Replication Management
class CouchReplicationManager {
    constructor() {
        this.nano = nano('http://localhost:5984');
    }

    // Setup replication
    async setupReplication(source, target, options = {}) {
        try {
            const replicationDoc = {
                _id: `replication_${Date.now()}`,
                source,
                target,
                create_target: options.createTarget || false,
                continuous: options.continuous || false,
                filter: options.filter,
                query_params: options.queryParams,
                doc_ids: options.docIds,
                ...options
            };

            const response = await this.nano.request({
                db: '_replicator',
                method: 'post',
                body: replicationDoc
            });

            console.log(`🔄 Replication setup initiated: ${response.id}`);
            return response;
        } catch (error) {
            console.error('❌ Error setting up replication:', error.message);
            throw error;
        }
    }

    // Monitor replication
    async getReplicationStatus(replicationId) {
        try {
            const replication = await this.nano.request({
                db: '_replicator',
                doc: replicationId
            });

            console.log(`📊 Replication status: ${replication.state}`, {
                source: replication.source,
                target: replication.target,
                state: replication.state,
                docs_read: replication.docs_read,
                docs_written: replication.docs_written,
                errors: replication.errors
            });

            return replication;
        } catch (error) {
            console.error('❌ Error getting replication status:', error.message);
            return null;
        }
    }
}

// Run the demonstration
if (require.main === module) {
    demonstrateCouchDB().catch(console.error);
}

module.exports = {
    CouchDBManager,
    CouchAttachmentManager,
    CouchChangeTracker,
    CouchReplicationManager
};

💻 Vistas Map-Reduce CouchDB javascript

🟡 intermediate ⭐⭐⭐⭐

Creación y uso de vistas Map-Reduce para agregación y análisis de datos

⏱️ 30 min 🏷️ couchdb, map-reduce, views, analytics, aggregation
Prerequisites: Basic CouchDB operations, JavaScript functions, Map-Reduce concepts
// CouchDB Map-Reduce Views
// JavaScript - Creating design documents with views and queries

const nano = require('nano')('http://localhost:5984');

class CouchViewManager {
    constructor(dbName) {
        this.db = nano.use(dbName);
    }

    // 1. Simple Map View
    async createSimpleMapViews() {
        const designDoc = {
            _id: '_design/simple',
            language: 'javascript',
            views: {
                // View to get all documents by type
                by_type: {
                    map: function(doc) {
                        if (doc.type) {
                            emit(doc.type, null);
                        }
                    }.toString()
                },

                // View to get all users by age
                users_by_age: {
                    map: function(doc) {
                        if (doc.type === 'user' && doc.age) {
                            emit(doc.age, doc.name);
                        }
                    }.toString()
                },

                // View to get products by price
                products_by_price: {
                    map: function(doc) {
                        if (doc.type === 'product' && doc.price) {
                            emit([doc.category, doc.price], doc);
                        }
                    }.toString()
                }
            }
        };

        const response = await this.db.insert(designDoc);
        console.log('✅ Simple views created:', response.id);
        return response;
    }

    // 2. Map-Reduce Views with Aggregation
    async createReduceViews() {
        const designDoc = {
            _id: '_design/analytics',
            language: 'javascript',
            views: {
                // Count documents by type
                count_by_type: {
                    map: function(doc) {
                        if (doc.type) {
                            emit(doc.type, 1);
                        }
                    }.toString(),
                    reduce: '_count'
                },

                // Sum prices by category
                total_price_by_category: {
                    map: function(doc) {
                        if (doc.type === 'product' && doc.category && doc.price) {
                            emit(doc.category, doc.price);
                        }
                    }.toString(),
                    reduce: '_sum'
                },

                // Average age by city
                avg_age_by_city: {
                    map: function(doc) {
                        if (doc.type === 'user' && doc.city && doc.age) {
                            emit(doc.city, doc.age);
                        }
                    }.toString(),
                    reduce: function(keys, values, rereduce) {
                        if (rereduce) {
                            // Re-reduce step
                            const totalSum = values.reduce((sum, item) => sum + item.sum, 0);
                            const totalCount = values.reduce((sum, item) => sum + item.count, 0);
                            return {
                                sum: totalSum,
                                count: totalCount,
                                avg: totalSum / totalCount
                            };
                        } else {
                            // First reduce step
                            const sum = values.reduce((a, b) => a + b, 0);
                            return {
                                sum: sum,
                                count: values.length,
                                avg: sum / values.length
                            };
                        }
                    }.toString()
                },

                // Statistics view with multiple reduce functions
                price_statistics: {
                    map: function(doc) {
                        if (doc.type === 'product' && doc.price) {
                            emit(doc.category, doc.price);
                        }
                    }.toString(),
                    reduce: function(keys, values, rereduce) {
                        if (rereduce) {
                            // Combine partial results
                            const allPrices = [];
                            values.forEach(item => {
                                if (item.prices) {
                                    allPrices.push(...item.prices);
                                }
                            });

                            return this.calculateStats(allPrices);
                        } else {
                            return this.calculateStats(values);
                        }
                    }.toString()
                }
            }
        };

        // Helper function for statistics
        designDoc.views.price_statistics.reduce = designDoc.views.price_statistics.replace(
            'this.calculateStats',
            `function(prices) {
                prices.sort((a, b) => a - b);
                const count = prices.length;
                const sum = prices.reduce((a, b) => a + b, 0);
                const mean = sum / count;
                const median = count % 2 === 0
                    ? (prices[count/2 - 1] + prices[count/2]) / 2
                    : prices[Math.floor(count/2)];

                return {
                    count: count,
                    sum: sum,
                    mean: mean,
                    median: median,
                    min: prices[0],
                    max: prices[count - 1],
                    prices: prices // Include for rereduce
                };
            }`
        );

const response = await this.db.insert(designDoc);
console.log('✅ Reduce views created:', response.id);
return response;
    }

    // 3. Complex Map Views
    async createComplexMapViews() {
    const designDoc = {
        _id: '_design/complex',
        language: 'javascript',
        views: {
            // View with compound keys
            users_by_city_and_age: {
                map: function (doc) {
                    if (doc.type === 'user' && doc.city && doc.age) {
                        emit([doc.city, Math.floor(doc.age / 10) * 10], {
                            name: doc.name,
                            age: doc.age,
                            email: doc.email
                        });
                    }
                }.toString()
            },

            // Time-series view
            activity_by_day: {
                map: function (doc) {
                    if (doc.created_at) {
                        var date = new Date(doc.created_at);
                        var dayKey = [date.getFullYear(), date.getMonth() + 1, date.getDate()];
                        emit(dayKey, {
                            type: doc.type,
                            hour: date.getHours()
                        });
                    }
                }.toString(),
                reduce: function (keys, values, rereduce) {
                    if (rereduce) {
                        return values.reduce((acc, curr) => {
                            Object.keys(curr).forEach(key => {
                                acc[key] = (acc[key] || 0) + curr[key];
                            });
                            return acc;
                        }, {});
                    } else {
                        const stats = {};
                        values.forEach(val => {
                            const type = val.type;
                            const hour = val.hour;

                            if (!stats[type]) stats[type] = { total: 0, hours: {} };
                            stats[type].total++;
                            stats[type].hours[hour] = (stats[type].hours[hour] || 0) + 1;
                        });
                        return stats;
                    }
                }.toString()
            },

            // View for full-text search (simple implementation)
            text_search: {
                map: function (doc) {
                    if (doc.type && doc.name) {
                        // Extract words from name and description
                        var text = doc.name + ' ' + (doc.description || '');
                        var words = text.toLowerCase().split(/\W+/);

                        words.forEach(function (word) {
                            if (word.length > 2) {
                                emit([word, doc.type], {
                                    id: doc._id,
                                    name: doc.name,
                                    type: doc.type
                                });
                            }
                        });
                    }
                }.toString()
            }
        }
    };

    const response = await this.db.insert(designDoc);
    console.log('✅ Complex views created:', response.id);
    return response;
}

    // 4. View Query Methods
    async queryView(dDocName, viewName, options = {}) {
    try {
        const response = await this.db.view(dDocName, viewName, options);
        return response;
    } catch (error) {
        console.error('❌ Error querying view:', error.message);
        throw error;
    }
}

    // Query examples
    async demonstrateQueries() {
    // Query users by specific age
    const users25to30 = await this.queryView('simple', 'users_by_age', {
        startkey: 25,
        endkey: 30,
        include_docs: true
    });

    console.log('Users aged 25-30:', users25to30.rows.length);

    // Query products by category range
    const electronics = await this.queryView('simple', 'products_by_price', {
        startkey: ['Electronics'],
        endkey: ['Electronics', {}],
        include_docs: true
    });

    console.log('Electronics products:', electronics.rows.length);

    // Get document counts by type
    const typeCounts = await this.queryView('analytics', 'count_by_type', {
        group: true
    });

    console.log('Document counts by type:');
    typeCounts.rows.forEach(row => {
        console.log(`  ${row.key}: ${row.value}`);
        });

        // Get total price by category
        const priceTotals = await this.queryView('analytics', 'total_price_by_category', {
            group: true
        });

        console.log('Total price by category:');
        priceTotals.rows.forEach(row => {
            console.log(`  ${row.key}: $${row.value.toFixed(2)}`);
        });

        // Get users by city and age groups
        const usersByCityAge = await this.queryView('complex', 'users_by_city_and_age', {
            group_level: 1,
            include_docs: true
        });

        console.log('Users by city:');
        usersByCityAge.rows.forEach(row => {
            console.log(`  ${row.key[0]}: ${row.value.length} users`);
        });
    }

    // 5. Advanced View Features
    async createAdvancedViews() {
        const designDoc = {
            _id: '_design/advanced',
            language: 'javascript',
            views: {
                // View with list function (using _list in URL)
                recent_activities: {
                    map: function(doc) {
                        if (doc.created_at && doc.type) {
                            emit(doc.created_at, {
                                _id: doc._id,
                                type: doc.type,
                                name: doc.name || doc.title || 'Unknown'
                            });
                        }
                    }.toString()
                },

                // View for filtering with built-in filters
                active_users: {
                    map: function(doc) {
                        if (doc.type === 'user' && doc.is_active !== false) {
                            emit(doc.email, {
                                name: doc.name,
                                last_login: doc.last_login,
                                created_at: doc.created_at
                            });
                        }
                    }.toString()
                },

                // View for relationship analysis
                user_connections: {
                    map: function(doc) {
                        if (doc.type === 'user' && doc.friends) {
                            doc.friends.forEach(function(friendId) {
                                emit([doc._id, friendId], {
                                    since: doc.friendship_since,
                                    strength: doc.friendship_strength || 1
                                });
                            });
                        }
                    }.toString(),
                    reduce: function(keys, values, rereduce) {
                        if (rereduce) {
                            return {
                                total_strength: values.reduce((sum, v) => sum + v.total_strength, 0),
                                count: values.reduce((sum, v) => sum + v.count, 0),
                                avg_strength: 0
                            };
                        } else {
                            const totalStrength = values.reduce((sum, v) => sum + v.strength, 0);
                            return {
                                total_strength: totalStrength,
                                count: values.length,
                                avg_strength: totalStrength / values.length
                            };
                        }
                    }.toString()
                }
            },
            // List functions for formatting output
            lists: {
                recent_activities_json: function(head, req) {
                    provides('json', function() {
                        var results = [];
                        while (row = getRow()) {
                            results.push({
                                id: row.value._id,
                                type: row.value.type,
                                name: row.value.name,
                                timestamp: row.key
                            });
                        }
                        send(JSON.stringify({
                            total_rows: head.total_rows,
                            offset: head.offset,
                            activities: results
                        }));
                    });
                }.toString()
            },
            // Show functions for document formatting
            shows: {
                user_profile: function(doc, req) {
                    if (doc.type === 'user') {
                        return {
                            html: '<h1>' + doc.name + '</h1>' +
                                  '<p>Email: ' + doc.email + '</p>' +
                                  '<p>Age: ' + doc.age + '</p>' +
                                  '<p>City: ' + doc.city + '</p>'
                        };
                    }
                    return null;
                }.toString()
            }
        };

        const response = await this.db.insert(designDoc);
        console.log('✅ Advanced views created:', response.id);
        return response;
    }

    // 6. View Performance Optimization
    async optimizeViews() {
        const designDoc = {
            _id: '_design/optimized',
            language: 'javascript',
            views: {
                // Efficient view with proper key design
                orders_by_date: {
                    map: function(doc) {
                        if (doc.type === 'order' && doc.date) {
                            // Emit date in ISO format for proper sorting
                            emit([doc.customer_id, doc.date], {
                                order_id: doc._id,
                                total: doc.total,
                                items: doc.items.length
                            });
                        }
                    }.toString()
                },

                // View with selective indexing
                indexed_products: {
                    map: function(doc) {
                        // Only index documents we actually need to query
                        if (doc.type === 'product' &&
                            doc.status === 'active' &&
                            doc.price > 0) {
                            emit(doc.category, {
                                id: doc._id,
                                name: doc.name,
                                price: doc.price,
                                rating: doc.rating || 0
                            });
                        }
                    }.toString()
                }
            },
            options: {
                // Automatic view indexing
                // Changes will automatically update views
            }
        };

        const response = await this.db.insert(designDoc);
        console.log('✅ Optimized views created:', response.id);
        return response;
    }
}

// Usage Example
async function demonstrateViews() {
    const viewManager = new CouchViewManager('analytics_db');

    // Create different types of views
    await viewManager.createSimpleMapViews();
    await viewManager.createReduceViews();
    await viewManager.createComplexMapViews();
    await viewManager.createAdvancedViews();
    await viewManager.optimizeViews();

    // Demonstrate queries
    await viewManager.demonstrateQueries();
}

if (require.main === module) {
    demonstrateViews().catch(console.error);
}

module.exports = CouchViewManager;

💻 Replicación y Sincronización CouchDB javascript

🔴 complex ⭐⭐⭐⭐

Configuración de replicación, resolución de conflictos y sincronización de datos

⏱️ 35 min 🏷️ couchdb, replication, sync, distributed systems, conflict resolution
Prerequisites: Advanced CouchDB operations, Network concepts, Conflict resolution strategies
// CouchDB Replication and Synchronization
// JavaScript - Advanced replication patterns and conflict resolution

const nano = require('nano')('http://localhost:5984');

class CouchReplicationManager {
    constructor() {
        this.nano = nano;
        this.replications = new Map();
    }

    // 1. Basic Replication Setup
    async setupOneTimeReplication(source, target, options = {}) {
        const replicationDoc = {
            _id: `replication_${Date.now()}`,
            source: source,
            target: target,
            create_target: options.createTarget || false,
            continuous: false,
            // Filter documents if needed
            filter: options.filter,
            query_params: options.queryParams,
            doc_ids: options.docIds
        };

        try {
            const response = await this.nano.request({
                db: '_replicator',
                method: 'post',
                body: replicationDoc
            });

            console.log(`✅ One-time replication started: ${response.id}`);
            this.replications.set(response.id, replicationDoc);
            return response;
        } catch (error) {
            console.error('❌ Error setting up one-time replication:', error.message);
            throw error;
        }
    }

    // 2. Continuous Replication
    async setupContinuousReplication(source, target, options = {}) {
        const replicationDoc = {
            _id: `continuous_${Date.now()}`,
            source: source,
            target: target,
            create_target: options.createTarget || false,
            continuous: true,
            // Replication options
            filter: options.filter,
            query_params: options.queryParams,
            doc_ids: options.docIds,
            // Heartbeat configuration
            heartbeat: options.heartbeat || 30000,
            // Worker processes
            worker_processes: options.workerProcesses || 1,
            // Batch size
            worker_batch_size: options.batchSize || 500,
            // Connection timeout
            timeout: options.timeout || 30000
        };

        try {
            const response = await this.nano.request({
                db: '_replicator',
                method: 'post',
                body: replicationDoc
            });

            console.log(`🔄 Continuous replication started: ${response.id}`);
            this.replications.set(response.id, replicationDoc);

            // Start monitoring
            this.monitorReplication(response.id);

            return response;
        } catch (error) {
            console.error('❌ Error setting up continuous replication:', error.message);
            throw error;
        }
    }

    // 3. Bi-directional Sync
    async setupBiDirectionalSync(db1, db2, options = {}) {
        const syncOptions = {
            ...options,
            continuous: true,
            create_target: false
        };

        // Replicate db1 -> db2
        const rep1 = await this.setupContinuousReplication(db1, db2, {
            ...syncOptions,
            _id: `sync_${db1}_to_${db2}_${Date.now()}`
        });

        // Replicate db2 -> db1
        const rep2 = await this.setupContinuousReplication(db2, db1, {
            ...syncOptions,
            _id: `sync_${db2}_to_${db1}_${Date.now()}`
        });

        console.log(`🔄 Bi-directional sync established between ${db1} and ${db2}`);

        return {
            forward: rep1,
            backward: rep2
        };
    }

    // 4. Filtered Replication
    async setupFilteredReplication(source, target, filterConfig) {
        // Create filter function in design document
        const filterDesignDoc = {
            _id: '_design/filters',
            filters: {
                by_type: function(doc, req) {
                    // Only replicate certain document types
                    const allowedTypes = req.query.types ?
                        req.query.types.split(',') :
                        ['user', 'product'];

                    return allowedTypes.includes(doc.type);
                }.toString(),

                by_date: function(doc, req) {
                    // Only replicate documents after certain date
                    if (!doc.created_at) return false;

                    const sinceDate = req.query.since || '1970-01-01';
                    const docDate = new Date(doc.created_at);
                    const filterDate = new Date(sinceDate);

                    return docDate >= filterDate;
                }.toString(),

                by_user: function(doc, req) {
                    // Only replicate user-specific data
                    if (doc.type === 'user') {
                        return doc._id === req.query.userId;
                    }
                    if (doc.owner) {
                        return doc.owner === req.query.userId;
                    }
                    if (doc.shared_with && doc.shared_with.includes(req.query.userId)) {
                        return true;
                    }
                    return false;
                }.toString()
            }
        };

        try {
            // Save filter design document
            const sourceDb = this.nano.use(source);
            await sourceDb.insert(filterDesignDoc);

            // Setup replication with filter
            return await this.setupContinuousReplication(source, target, {
                filter: 'filters/by_' + filterConfig.type,
                query_params: filterConfig.params
            });

        } catch (error) {
            console.error('❌ Error setting up filtered replication:', error.message);
            throw error;
        }
    }

    // 5. Replication Monitoring
    async monitorReplication(replicationId) {
        const checkInterval = 5000; // Check every 5 seconds

        const monitor = setInterval(async () => {
            try {
                const status = await this.getReplicationStatus(replicationId);

                if (status) {
                    console.log(`📊 Replication ${replicationId} status: ${status.state}`, {
                        docs_read: status.docs_read || 0,
                        docs_written: status.docs_written || 0,
                        errors: status.errors || 0,
                        progress: status.progress || 0
                    });

                    // Stop monitoring if replication completes
                    if (status.state === 'completed' || status.state === 'error') {
                        clearInterval(monitor);
                        console.log(`🏁 Replication ${replicationId} finished with status: ${status.state}`);
                    }
                }
            } catch (error) {
                console.error('❌ Error monitoring replication:', error.message);
                clearInterval(monitor);
            }
        }, checkInterval);
    }

    // Get replication status
    async getReplicationStatus(replicationId) {
        try {
            const replication = await this.nano.request({
                db: '_replicator',
                doc: replicationId
            });

            return replication;
        } catch (error) {
            if (error.statusCode === 404) {
                console.log(`⚠️ Replication ${replicationId} not found`);
            } else {
                console.error('❌ Error getting replication status:', error.message);
            }
            return null;
        }
    }

    // 6. Conflict Resolution
    async resolveConflicts(dbName, conflictResolver) {
        const db = this.nano.use(dbName);

        // Get all documents with conflicts
        const conflicts = await db.view('_docs_with_conflicts', 'conflicts', {
            include_docs: true
        });

        console.log(`⚠️ Found ${conflicts.rows.length} documents with conflicts`);

        for (const row of conflicts.rows) {
            const doc = row.doc;
            const conflictingRevs = doc._conflicts;

            if (conflictingRevs && conflictingRevs.length > 0) {
                console.log(`🔄 Resolving conflicts for document: ${doc._id}`);

                // Get all conflicting revisions
                const revisions = [];
                for (const rev of conflictingRevs) {
                    try {
                        const conflictDoc = await db.get(doc._id, { rev: rev });
                        revisions.push(conflictDoc);
                    } catch (error) {
                        console.error(`❌ Error getting revision ${rev}:`, error.message);
                    }
                }

                // Use custom conflict resolver
                const resolvedDoc = await conflictResolver(doc, revisions);

                if (resolvedDoc) {
                    // Save resolved document
                    try {
                        const response = await db.insert(resolvedDoc);
                        console.log(`✅ Conflict resolved for ${doc._id}: ${response.rev}`);
                    } catch (error) {
                        console.error(`❌ Error saving resolved document ${doc._id}:`, error.message);
                    }
                }
            }
        }
    }

    // Default conflict resolution strategies
    static conflictResolvers = {
        // Use most recent document based on timestamp
        latestWins: async (currentDoc, conflicts) => {
            let latestDoc = currentDoc;
            let latestTime = new Date(currentDoc.updated_at || currentDoc.created_at || 0);

            for (const conflict of conflicts) {
                const conflictTime = new Date(conflict.updated_at || conflict.created_at || 0);
                if (conflictTime > latestTime) {
                    latestTime = conflictTime;
                    latestDoc = conflict;
                }
            }

            // Clean up conflicts
            delete latestDoc._conflicts;
            return latestDoc;
        },

        // Merge documents (for specific types)
        merge: async (currentDoc, conflicts) => {
            // Simple merge strategy
            const merged = { ...currentDoc };

            for (const conflict of conflicts) {
                // Merge properties from conflicts
                Object.keys(conflict).forEach(key => {
                    if (key !== '_id' && key !== '_rev' && key !== '_conflicts') {
                        if (!merged[key] ||
                            (conflict.updated_at && (!merged.updated_at || new Date(conflict.updated_at) > new Date(merged.updated_at)))) {
                            merged[key] = conflict[key];
                        }
                    }
                });
            }

            delete merged._conflicts;
            return merged;
        },

        // Keep document with highest version number
        versionBased: async (currentDoc, conflicts) => {
            let versionDoc = currentDoc;
            let highestVersion = parseInt(currentDoc.version || '1');

            for (const conflict of conflicts) {
                const conflictVersion = parseInt(conflict.version || '1');
                if (conflictVersion > highestVersion) {
                    highestVersion = conflictVersion;
                    versionDoc = conflict;
                }
            }

            delete versionDoc._conflicts;
            versionDoc.version = (highestVersion + 1).toString();
            return versionDoc;
        }
    };

    // 7. Sync Status Management
    async getSyncStatus(sourceDb, targetDb) {
        try {
            // Get info from both databases
            const sourceInfo = await this.nano.db.info(sourceDb);
            const targetInfo = await this.nano.db.info(targetDb);

            // Get replication tasks
            const activeTasks = await this.nano.request({
                path: '_active_tasks',
                method: 'get'
            });

            const replications = activeTasks.filter(task =>
                task.type === 'replication' &&
                (task.source === sourceDb || task.target === targetDb)
            );

            return {
                source: {
                    name: sourceDb,
                    doc_count: sourceInfo.doc_count,
                    update_seq: sourceInfo.update_seq
                },
                target: {
                    name: targetDb,
                    doc_count: targetInfo.doc_count,
                    update_seq: targetInfo.update_seq
                },
                active_replications: replications.map(rep => ({
                    task_id: rep.task_id,
                    source: rep.source,
                    target: rep.target,
                    state: rep.state,
                    progress: rep.progress,
                    docs_read: rep.docs_read,
                    docs_written: rep.docs_written
                }))
            };
        } catch (error) {
            console.error('❌ Error getting sync status:', error.message);
            return null;
        }
    }

    // 8. Backup and Restore with Replication
    async setupBackupReplication(sourceDb, backupDb, options = {}) {
        const backupOptions = {
            ...options,
            continuous: options.continuousBackup || false,
            create_target: true,
            // Backup filter
            filter: function(doc) {
                // Don't replicate temporary or design documents unless specified
                if (doc._id && doc._id.startsWith('_temp/')) return false;
                if (doc._id && doc._id.startsWith('_design/') && !options.includeDesignDocs) return false;
                return true;
            }.toString(),
            query_params: {
                include_design_docs: options.includeDesignDocs || false
            }
        };

        return await this.setupContinuousReplication(sourceDb, backupDb, backupOptions);
    }

    // 9. Cleanup and Maintenance
    async cleanupReplication(replicationId) {
        try {
            // Stop the replication
            await this.nano.request({
                db: '_replicator',
                doc: replicationId,
                method: 'delete'
            });

            this.replications.delete(replicationId);
            console.log(`🗑️ Replication ${replicationId} stopped and cleaned up`);

            return true;
        } catch (error) {
            console.error('❌ Error cleaning up replication:', error.message);
            return false;
        }
    }

    // Cleanup old replications
    async cleanupOldReplications(maxAge = 24 * 60 * 60 * 1000) { // Default 24 hours
        try {
            const replications = await this.nano.request({
                db: '_replicator',
                method: 'get'
            });

            const now = Date.now();
            const oldReplications = [];

            for (const rep of replications.rows) {
                const repDoc = rep.doc;
                const timestamp = parseInt(repDoc._id.split('_').pop());

                if (now - timestamp > maxAge) {
                    oldReplications.push(repDoc._id);
                }
            }

            // Delete old replications
            for (const repId of oldReplications) {
                await this.cleanupReplication(repId);
            }

            console.log(`🗑️ Cleaned up ${oldReplications.length} old replications`);

            return oldReplications.length;
        } catch (error) {
            console.error('❌ Error cleaning up old replications:', error.message);
            return 0;
        }
    }
}

// Usage Example
async function demonstrateReplication() {
    const repManager = new CouchReplicationManager();

    // Setup one-time replication
    await repManager.setupOneTimeReplication('source_db', 'backup_db', {
        createTarget: true
    });

    // Setup continuous replication
    await repManager.setupContinuousReplication('source_db', 'replica_db', {
        createTarget: true
    });

    // Setup bi-directional sync
    await repManager.setupBiDirectionalSync('db1', 'db2');

    // Setup filtered replication
    await repManager.setupFilteredReplication('source_db', 'filtered_replica', {
        type: 'by_user',
        params: { userId: 'user123' }
    });

    // Resolve conflicts
    await repManager.resolveConflicts('sync_db', CouchReplicationManager.conflictResolvers.latestWins);

    // Get sync status
    const syncStatus = await repManager.getSyncStatus('source_db', 'replica_db');
    console.log('Sync status:', JSON.stringify(syncStatus, null, 2));

    // Setup backup replication
    await repManager.setupBackupReplication('production_db', 'backup_db', {
        continuousBackup: true,
        includeDesignDocs: true
    });
}

if (require.main === module) {
    demonstrateReplication().catch(console.error);
}

module.exports = CouchReplicationManager;