Created
October 1, 2024 01:09
-
-
Save rightson/d86d59a61c47ee631a007e9d9b1449ab to your computer and use it in GitHub Desktop.
MongoDB Database Copy Script (alternative to mongodump/mongorestore)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MongoDB Refactored Copy Script with Modular Functions and Index Copying | |
// This script copies all collections and their indexes from a source database to a target database without using mongodump. | |
// === Configuration === | |
// Source MongoDB connection details | |
const sourceConfig = { | |
host: "source_host", // e.g., "localhost" | |
port: 27017, // e.g., 27017 | |
username: "sourceUser", // Source DB username | |
password: "sourcePassword", // Source DB password | |
authDb: "admin", // Authentication database for source (often "admin" or the db itself) | |
dbName: "sourceDatabase" // Source database name to copy | |
}; | |
// Target MongoDB connection details | |
const targetConfig = { | |
host: "target_host", // e.g., "localhost" | |
port: 27017, // e.g., 27017 | |
username: "targetUser", // Target DB username (if authentication is required) | |
password: "targetPassword", // Target DB password | |
authDb: "admin", // Authentication database for target (if authentication is required) | |
dbName: "targetDatabase" // Target database name where data will be copied | |
}; | |
// Batch size for bulk insert operations | |
const BATCH_SIZE = 1000; | |
// === Helper Functions === | |
/** | |
* Constructs a MongoDB URI with proper encoding for username and password. | |
* @param {Object} config - Configuration object containing connection details. | |
* @returns {String} - Constructed MongoDB URI. | |
*/ | |
function buildMongoURI(config) { | |
// Encode username and password to handle special characters | |
const encodedUsername = encodeURIComponent(config.username); | |
const encodedPassword = encodeURIComponent(config.password); | |
// Construct the URI with authentication | |
return `mongodb://${encodedUsername}:${encodedPassword}@${config.host}:${config.port}/${config.dbName}?authSource=${config.authDb}`; | |
} | |
/** | |
* Connects to a MongoDB instance using the provided URI. | |
* @param {String} uri - MongoDB connection URI. | |
* @param {String} dbName - Database name to connect to. | |
* @returns {Object} - Connected MongoDB database object. | |
*/ | |
function connectToDatabase(uri, dbName) { | |
try { | |
const conn = new Mongo(uri); | |
const db = conn.getDB(dbName); | |
// Verify connection | |
db.runCommand({ ping: 1 }); | |
log(`Successfully connected to database: ${dbName}`); | |
return { conn, db }; | |
} catch (e) { | |
log(`Failed to connect to database '${dbName}': ${e}`); | |
quit(1); | |
} | |
} | |
/** | |
* Retrieves all collection names from a given database, excluding system collections. | |
* @param {Object} db - MongoDB database object. | |
* @returns {Array} - List of collection names. | |
*/ | |
function getCollections(db) { | |
return db.getCollectionNames().filter(function(collName) { | |
return !collName.startsWith("system."); | |
}); | |
} | |
/** | |
* Copies all indexes from a source collection to a target collection. | |
* @param {Object} sourceCollection - Source MongoDB collection object. | |
* @param {Object} targetCollection - Target MongoDB collection object. | |
*/ | |
function copyIndexes(sourceCollection, targetCollection) { | |
log(` Copying indexes for collection: ${sourceCollection.getName()}`); | |
const indexes = sourceCollection.getIndexes(); | |
indexes.forEach(function(index) { | |
// Skip the default _id index as MongoDB automatically creates it | |
if (index.name === "_id_") { | |
log(` Skipping default _id index.`); | |
return; | |
} | |
// Prepare index options by excluding fields not needed for creation | |
const indexOptions = Object.assign({}, index); | |
delete indexOptions.v; | |
delete indexOptions.ns; | |
delete indexOptions.key; | |
delete indexOptions.name; | |
try { | |
targetCollection.createIndex(index.key, indexOptions); | |
log(` Created index: ${index.name}`); | |
} catch (e) { | |
log(` Error creating index '${index.name}': ${e}`); | |
} | |
}); | |
} | |
/** | |
* Copies all documents from a source collection to a target collection in batches. | |
* @param {Object} sourceCollection - Source MongoDB collection object. | |
* @param {Object} targetCollection - Target MongoDB collection object. | |
*/ | |
function copyCollectionData(sourceCollection, targetCollection) { | |
log(` Starting data copy for collection: ${sourceCollection.getName()}`); | |
const cursor = sourceCollection.find(); | |
let batch = []; | |
let totalInserted = 0; | |
while (cursor.hasNext()) { | |
const doc = cursor.next(); | |
batch.push(doc); | |
if (batch.length === BATCH_SIZE) { | |
try { | |
targetCollection.insertMany(batch); | |
totalInserted += batch.length; | |
log(` Inserted ${totalInserted} documents so far...`); | |
batch = []; | |
} catch (e) { | |
log(` Error inserting batch: ${e}`); | |
} | |
} | |
} | |
// Insert any remaining documents in the batch | |
if (batch.length > 0) { | |
try { | |
targetCollection.insertMany(batch); | |
totalInserted += batch.length; | |
log(` Inserted a total of ${totalInserted} documents into '${sourceCollection.getName()}'.`); | |
} catch (e) { | |
log(` Error inserting final batch: ${e}`); | |
} | |
} | |
log(` Completed data copy for collection: ${sourceCollection.getName()}`); | |
} | |
/** | |
* Logs messages with a timestamp for better traceability. | |
* @param {String} message - Message to log. | |
*/ | |
function log(message) { | |
const timestamp = new Date().toISOString(); | |
print(`[${timestamp}] ${message}`); | |
} | |
// === Main Execution Flow === | |
(function() { | |
// Build connection URIs | |
const sourceUri = buildMongoURI(sourceConfig); | |
const targetUri = buildMongoURI(targetConfig); | |
// Connect to source and target databases | |
log("Connecting to source MongoDB..."); | |
const { conn: sourceConn, db: sourceDB } = connectToDatabase(sourceUri, sourceConfig.dbName); | |
log("Connecting to target MongoDB..."); | |
const { conn: targetConn, db: targetDB } = connectToDatabase(targetUri, targetConfig.dbName); | |
// Retrieve collections from source database | |
const collections = getCollections(sourceDB); | |
log(`\nFound ${collections.length} collections to copy from '${sourceConfig.dbName}' to '${targetConfig.dbName}'.`); | |
// Iterate through each collection and perform copy operations | |
collections.forEach(function(collName) { | |
log(`\nProcessing collection: ${collName}`); | |
const sourceCollection = sourceDB.getCollection(collName); | |
const targetCollection = targetDB.getCollection(collName); | |
// Optional: Drop the target collection if it exists to start fresh | |
if (targetDB.getCollectionNames().includes(collName)) { | |
log(` Dropping existing collection '${collName}' in target database.`); | |
targetCollection.drop(); | |
} | |
// Create the collection in the target database | |
try { | |
targetDB.createCollection(collName); | |
log(` Created collection '${collName}' in target database.`); | |
} catch (e) { | |
log(` Error creating collection '${collName}': ${e}`); | |
} | |
// Copy indexes | |
copyIndexes(sourceCollection, targetCollection); | |
// Copy documents | |
copyCollectionData(sourceCollection, targetCollection); | |
log(`Finished processing collection: ${collName}`); | |
}); | |
log(`\nDatabase copy from '${sourceConfig.dbName}' to '${targetConfig.dbName}' completed successfully.`); | |
// Close the connections | |
sourceConn.close(); | |
targetConn.close(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment