refactor: remove MongoDB legacy code and references
- Archive migration script to scripts/archive/migrate-to-couchdb.js - Update error handler middleware for CouchDB-appropriate errors - Fix MongoDB references in test utilities and comments - Replace MongoDB ObjectId references with CouchDB ID patterns - Preserve existing functionality while removing legacy dependencies 🤖 Generated with [AI Assistant] Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
This commit is contained in:
651
scripts/archive/migrate-to-couchdb.js
Normal file
651
scripts/archive/migrate-to-couchdb.js
Normal file
@@ -0,0 +1,651 @@
|
||||
// Setup module path to include backend node_modules
|
||||
const path = require('path');
|
||||
const backendPath = path.join(__dirname, '..', 'backend');
|
||||
process.env.NODE_PATH = path.join(backendPath, 'node_modules') + ':' + (process.env.NODE_PATH || '');
|
||||
require('module').Module._initPaths();
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const Nano = require('nano');
|
||||
|
||||
// MongoDB models (only needed for migration)
|
||||
const User = require('../backend/models/User');
|
||||
const Street = require('../backend/models/Street');
|
||||
const Task = require('../backend/models/Task');
|
||||
const Post = require('../backend/models/Post');
|
||||
const Event = require('../backend/models/Event');
|
||||
const Report = require('../backend/models/Report');
|
||||
const Badge = require('../backend/models/Badge');
|
||||
const Comment = require('../backend/models/Comment');
|
||||
const PointTransaction = require('../backend/models/PointTransaction');
|
||||
const UserBadge = require('../backend/models/UserBadge');
|
||||
|
||||
// Configuration
|
||||
const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/adopt-a-street';
|
||||
const COUCHDB_URL = process.env.COUCHDB_URL || 'http://localhost:5984';
|
||||
const COUCHDB_DB = process.env.COUCHDB_DB || 'adopt-a-street';
|
||||
|
||||
class MongoToCouchMigrator {
|
||||
constructor() {
|
||||
this.nano = Nano(COUCHDB_URL);
|
||||
this.db = null;
|
||||
this.migrationStats = {
|
||||
users: 0,
|
||||
streets: 0,
|
||||
tasks: 0,
|
||||
posts: 0,
|
||||
events: 0,
|
||||
reports: 0,
|
||||
badges: 0,
|
||||
comments: 0,
|
||||
pointTransactions: 0,
|
||||
userBadges: 0,
|
||||
errors: []
|
||||
};
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
console.log('🚀 Initializing migration...');
|
||||
|
||||
// Connect to MongoDB
|
||||
await mongoose.connect(MONGO_URI);
|
||||
console.log('✅ Connected to MongoDB');
|
||||
|
||||
// Initialize CouchDB
|
||||
try {
|
||||
this.db = this.nano.db.use(COUCHDB_DB);
|
||||
console.log('✅ Connected to existing CouchDB database');
|
||||
} catch (error) {
|
||||
if (error.statusCode === 404) {
|
||||
await this.nano.db.create(COUCHDB_DB);
|
||||
this.db = this.nano.db.use(COUCHDB_DB);
|
||||
console.log('✅ Created new CouchDB database');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Create indexes
|
||||
await this.createIndexes();
|
||||
console.log('✅ Created CouchDB indexes');
|
||||
}
|
||||
|
||||
async createIndexes() {
|
||||
const indexes = [
|
||||
{
|
||||
index: { fields: ['type', 'email'] },
|
||||
name: 'user-by-email',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'location'] },
|
||||
name: 'streets-by-location',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'user.userId'] },
|
||||
name: 'by-user',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'points'] },
|
||||
name: 'users-by-points',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'createdAt'] },
|
||||
name: 'posts-by-date',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'status'] },
|
||||
name: 'streets-by-status',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'date', 'status'] },
|
||||
name: 'events-by-date-status',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'post.postId'] },
|
||||
name: 'comments-by-post',
|
||||
type: 'json'
|
||||
},
|
||||
{
|
||||
index: { fields: ['type', 'user.userId', 'createdAt'] },
|
||||
name: 'transactions-by-user-date',
|
||||
type: 'json'
|
||||
}
|
||||
];
|
||||
|
||||
for (const index of indexes) {
|
||||
try {
|
||||
await this.db.createIndex(index);
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Index creation failed: ${index.name}`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transformUser(mongoUser) {
|
||||
return {
|
||||
_id: `user_${mongoUser._id}`,
|
||||
type: 'user',
|
||||
name: mongoUser.name,
|
||||
email: mongoUser.email,
|
||||
password: mongoUser.password,
|
||||
isPremium: mongoUser.isPremium || false,
|
||||
points: mongoUser.points || 0,
|
||||
profilePicture: mongoUser.profilePicture,
|
||||
cloudinaryPublicId: mongoUser.cloudinaryPublicId,
|
||||
adoptedStreets: mongoUser.adoptedStreets.map(id => `street_${id}`),
|
||||
completedTasks: mongoUser.completedTasks.map(id => `task_${id}`),
|
||||
posts: mongoUser.posts.map(id => `post_${id}`),
|
||||
events: mongoUser.events.map(id => `event_${id}`),
|
||||
earnedBadges: [], // Will be populated from UserBadge collection
|
||||
stats: {
|
||||
streetsAdopted: mongoUser.adoptedStreets.length,
|
||||
tasksCompleted: mongoUser.completedTasks.length,
|
||||
postsCreated: mongoUser.posts.length,
|
||||
eventsParticipated: mongoUser.events.length,
|
||||
badgesEarned: 0
|
||||
},
|
||||
createdAt: mongoUser.createdAt,
|
||||
updatedAt: mongoUser.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformStreet(mongoStreet) {
|
||||
return {
|
||||
_id: `street_${mongoStreet._id}`,
|
||||
type: 'street',
|
||||
name: mongoStreet.name,
|
||||
location: mongoStreet.location,
|
||||
adoptedBy: mongoStreet.adoptedBy ? {
|
||||
userId: `user_${mongoStreet.adoptedBy}`,
|
||||
name: '', // Will be populated from user data
|
||||
profilePicture: ''
|
||||
} : null,
|
||||
status: mongoStreet.status,
|
||||
stats: {
|
||||
tasksCount: 0, // Will be calculated
|
||||
completedTasksCount: 0, // Will be calculated
|
||||
reportsCount: 0, // Will be calculated
|
||||
openReportsCount: 0 // Will be calculated
|
||||
},
|
||||
createdAt: mongoStreet.createdAt,
|
||||
updatedAt: mongoStreet.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformTask(mongoTask) {
|
||||
return {
|
||||
_id: `task_${mongoTask._id}`,
|
||||
type: 'task',
|
||||
street: {
|
||||
streetId: `street_${mongoTask.street}`,
|
||||
name: '', // Will be populated from street data
|
||||
location: null // Will be populated from street data
|
||||
},
|
||||
description: mongoTask.description,
|
||||
completedBy: mongoTask.completedBy ? {
|
||||
userId: `user_${mongoTask.completedBy}`,
|
||||
name: '', // Will be populated from user data
|
||||
profilePicture: ''
|
||||
} : null,
|
||||
status: mongoTask.status,
|
||||
completedAt: mongoTask.updatedAt, // Approximation
|
||||
pointsAwarded: 10, // Default, could be made configurable
|
||||
createdAt: mongoTask.createdAt,
|
||||
updatedAt: mongoTask.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformPost(mongoPost) {
|
||||
return {
|
||||
_id: `post_${mongoPost._id}`,
|
||||
type: 'post',
|
||||
user: {
|
||||
userId: `user_${mongoPost.user}`,
|
||||
name: '', // Will be populated from user data
|
||||
profilePicture: ''
|
||||
},
|
||||
content: mongoPost.content,
|
||||
imageUrl: mongoPost.imageUrl,
|
||||
cloudinaryPublicId: mongoPost.cloudinaryPublicId,
|
||||
likes: mongoPost.likes.map(id => `user_${id}`),
|
||||
likesCount: mongoPost.likes.length,
|
||||
commentsCount: mongoPost.commentsCount || 0,
|
||||
createdAt: mongoPost.createdAt,
|
||||
updatedAt: mongoPost.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformEvent(mongoEvent) {
|
||||
return {
|
||||
_id: `event_${mongoEvent._id}`,
|
||||
type: 'event',
|
||||
title: mongoEvent.title,
|
||||
description: mongoEvent.description,
|
||||
date: mongoEvent.date,
|
||||
location: mongoEvent.location,
|
||||
participants: mongoEvent.participants.map(userId => ({
|
||||
userId: `user_${userId}`,
|
||||
name: '', // Will be populated from user data
|
||||
profilePicture: '',
|
||||
joinedAt: mongoEvent.createdAt // Approximation
|
||||
})),
|
||||
participantsCount: mongoEvent.participants.length,
|
||||
status: mongoEvent.status,
|
||||
createdAt: mongoEvent.createdAt,
|
||||
updatedAt: mongoEvent.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformReport(mongoReport) {
|
||||
return {
|
||||
_id: `report_${mongoReport._id}`,
|
||||
type: 'report',
|
||||
street: {
|
||||
streetId: `street_${mongoReport.street}`,
|
||||
name: '', // Will be populated from street data
|
||||
location: null // Will be populated from street data
|
||||
},
|
||||
user: {
|
||||
userId: `user_${mongoReport.user}`,
|
||||
name: '', // Will be populated from user data
|
||||
profilePicture: ''
|
||||
},
|
||||
issue: mongoReport.issue,
|
||||
imageUrl: mongoReport.imageUrl,
|
||||
cloudinaryPublicId: mongoReport.cloudinaryPublicId,
|
||||
status: mongoReport.status,
|
||||
createdAt: mongoReport.createdAt,
|
||||
updatedAt: mongoReport.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformBadge(mongoBadge) {
|
||||
return {
|
||||
_id: `badge_${mongoBadge._id}`,
|
||||
type: 'badge',
|
||||
name: mongoBadge.name,
|
||||
description: mongoBadge.description,
|
||||
icon: mongoBadge.icon,
|
||||
criteria: mongoBadge.criteria,
|
||||
rarity: mongoBadge.rarity,
|
||||
order: mongoBadge.order || 0,
|
||||
isActive: true,
|
||||
createdAt: mongoBadge.createdAt,
|
||||
updatedAt: mongoBadge.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformComment(mongoComment) {
|
||||
return {
|
||||
_id: `comment_${mongoComment._id}`,
|
||||
type: 'comment',
|
||||
post: {
|
||||
postId: `post_${mongoComment.post}`,
|
||||
content: '', // Will be populated from post data
|
||||
userId: '' // Will be populated from post data
|
||||
},
|
||||
user: {
|
||||
userId: `user_${mongoComment.user}`,
|
||||
name: '', // Will be populated from user data
|
||||
profilePicture: ''
|
||||
},
|
||||
content: mongoComment.content,
|
||||
createdAt: mongoComment.createdAt,
|
||||
updatedAt: mongoComment.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
transformPointTransaction(mongoTransaction) {
|
||||
return {
|
||||
_id: `transaction_${mongoTransaction._id}`,
|
||||
type: 'point_transaction',
|
||||
user: {
|
||||
userId: `user_${mongoTransaction.user}`,
|
||||
name: '' // Will be populated from user data
|
||||
},
|
||||
amount: mongoTransaction.amount,
|
||||
type: mongoTransaction.type,
|
||||
description: mongoTransaction.description,
|
||||
relatedEntity: mongoTransaction.relatedEntity ? {
|
||||
entityType: mongoTransaction.relatedEntity.entityType,
|
||||
entityId: mongoTransaction.relatedEntity.entityId ?
|
||||
`${mongoTransaction.relatedEntity.entityType.toLowerCase()}_${mongoTransaction.relatedEntity.entityId}` : null,
|
||||
entityName: '' // Will be populated if possible
|
||||
} : null,
|
||||
balanceAfter: mongoTransaction.balanceAfter,
|
||||
createdAt: mongoTransaction.createdAt
|
||||
};
|
||||
}
|
||||
|
||||
transformUserBadge(mongoUserBadge) {
|
||||
return {
|
||||
_id: `userbadge_${mongoUserBadge._id}`,
|
||||
type: 'user_badge',
|
||||
userId: `user_${mongoUserBadge.user}`,
|
||||
badgeId: `badge_${mongoUserBadge.badge}`,
|
||||
earnedAt: mongoUserBadge.earnedAt,
|
||||
progress: mongoUserBadge.progress,
|
||||
createdAt: mongoUserBadge.createdAt,
|
||||
updatedAt: mongoUserBadge.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
async migrateCollection(mongoModel, transformer, collectionName) {
|
||||
console.log(`📦 Migrating ${collectionName}...`);
|
||||
|
||||
try {
|
||||
const documents = await mongoModel.find().lean();
|
||||
const transformedDocs = documents.map(transformer.bind(this));
|
||||
|
||||
// Batch insert to CouchDB
|
||||
const batchSize = 100;
|
||||
for (let i = 0; i < transformedDocs.length; i += batchSize) {
|
||||
const batch = transformedDocs.slice(i, i + batchSize);
|
||||
|
||||
for (const doc of batch) {
|
||||
try {
|
||||
await this.db.insert(doc);
|
||||
this.migrationStats[collectionName]++;
|
||||
} catch (error) {
|
||||
if (error.statusCode === 409) {
|
||||
// Document already exists, update it
|
||||
const existing = await this.db.get(doc._id);
|
||||
doc._rev = existing._rev;
|
||||
await this.db.insert(doc);
|
||||
this.migrationStats[collectionName]++;
|
||||
} else {
|
||||
console.error(`❌ Error inserting ${collectionName} ${doc._id}:`, error.message);
|
||||
this.migrationStats.errors.push(`${collectionName} ${doc._id}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Migrated ${this.migrationStats[collectionName]} ${collectionName}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error migrating ${collectionName}:`, error.message);
|
||||
this.migrationStats.errors.push(`Collection ${collectionName}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async populateRelationships() {
|
||||
console.log('🔗 Populating relationships...');
|
||||
|
||||
// Get all users for lookup
|
||||
const users = await this.db.find({
|
||||
selector: { type: 'user' },
|
||||
fields: ['_id', 'name', 'profilePicture']
|
||||
});
|
||||
|
||||
const userMap = {};
|
||||
users.docs.forEach(user => {
|
||||
userMap[user._id] = {
|
||||
name: user.name,
|
||||
profilePicture: user.profilePicture || ''
|
||||
};
|
||||
});
|
||||
|
||||
// Get all streets for lookup
|
||||
const streets = await this.db.find({
|
||||
selector: { type: 'street' },
|
||||
fields: ['_id', 'name', 'location']
|
||||
});
|
||||
|
||||
const streetMap = {};
|
||||
streets.docs.forEach(street => {
|
||||
streetMap[street._id] = {
|
||||
name: street.name,
|
||||
location: street.location
|
||||
};
|
||||
});
|
||||
|
||||
// Update streets with adopter info
|
||||
for (const street of streets.docs) {
|
||||
if (street.adoptedBy) {
|
||||
const adopterInfo = userMap[street.adoptedBy.userId];
|
||||
if (adopterInfo) {
|
||||
street.adoptedBy.name = adopterInfo.name;
|
||||
street.adoptedBy.profilePicture = adopterInfo.profilePicture;
|
||||
await this.db.insert(street);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update tasks with street and user info
|
||||
const tasks = await this.db.find({
|
||||
selector: { type: 'task' },
|
||||
fields: ['_id', 'street', 'completedBy']
|
||||
});
|
||||
|
||||
for (const task of tasks.docs) {
|
||||
let updated = false;
|
||||
|
||||
if (task.street && streetMap[task.street.streetId]) {
|
||||
task.street.name = streetMap[task.street.streetId].name;
|
||||
task.street.location = streetMap[task.street.streetId].location;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (task.completedBy && userMap[task.completedBy.userId]) {
|
||||
task.completedBy.name = userMap[task.completedBy.userId].name;
|
||||
task.completedBy.profilePicture = userMap[task.completedBy.userId].profilePicture;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
await this.db.insert(task);
|
||||
}
|
||||
}
|
||||
|
||||
// Update posts with user info
|
||||
const posts = await this.db.find({
|
||||
selector: { type: 'post' },
|
||||
fields: ['_id', 'user']
|
||||
});
|
||||
|
||||
for (const post of posts.docs) {
|
||||
if (userMap[post.user.userId]) {
|
||||
post.user.name = userMap[post.user.userId].name;
|
||||
post.user.profilePicture = userMap[post.user.userId].profilePicture;
|
||||
await this.db.insert(post);
|
||||
}
|
||||
}
|
||||
|
||||
// Update events with participant info
|
||||
const events = await this.db.find({
|
||||
selector: { type: 'event' },
|
||||
fields: ['_id', 'participants']
|
||||
});
|
||||
|
||||
for (const event of events.docs) {
|
||||
let updated = false;
|
||||
for (const participant of event.participants) {
|
||||
if (userMap[participant.userId]) {
|
||||
participant.name = userMap[participant.userId].name;
|
||||
participant.profilePicture = userMap[participant.userId].profilePicture;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
await this.db.insert(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Update reports with street and user info
|
||||
const reports = await this.db.find({
|
||||
selector: { type: 'report' },
|
||||
fields: ['_id', 'street', 'user']
|
||||
});
|
||||
|
||||
for (const report of reports.docs) {
|
||||
let updated = false;
|
||||
|
||||
if (report.street && streetMap[report.street.streetId]) {
|
||||
report.street.name = streetMap[report.street.streetId].name;
|
||||
report.street.location = streetMap[report.street.streetId].location;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (report.user && userMap[report.user.userId]) {
|
||||
report.user.name = userMap[report.user.userId].name;
|
||||
report.user.profilePicture = userMap[report.user.userId].profilePicture;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
await this.db.insert(report);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Relationships populated');
|
||||
}
|
||||
|
||||
async calculateStats() {
|
||||
console.log('📊 Calculating statistics...');
|
||||
|
||||
// Calculate street stats
|
||||
const streets = await this.db.find({
|
||||
selector: { type: 'street' },
|
||||
fields: ['_id']
|
||||
});
|
||||
|
||||
for (const street of streets.docs) {
|
||||
const tasks = await this.db.find({
|
||||
selector: {
|
||||
type: 'task',
|
||||
'street.streetId': street._id
|
||||
},
|
||||
fields: ['status']
|
||||
});
|
||||
|
||||
const reports = await this.db.find({
|
||||
selector: {
|
||||
type: 'report',
|
||||
'street.streetId': street._id
|
||||
},
|
||||
fields: ['status']
|
||||
});
|
||||
|
||||
const streetDoc = await this.db.get(street._id);
|
||||
streetDoc.stats = {
|
||||
tasksCount: tasks.docs.length,
|
||||
completedTasksCount: tasks.docs.filter(t => t.status === 'completed').length,
|
||||
reportsCount: reports.docs.length,
|
||||
openReportsCount: reports.docs.filter(r => r.status === 'open').length
|
||||
};
|
||||
|
||||
await this.db.insert(streetDoc);
|
||||
}
|
||||
|
||||
// Populate user badges
|
||||
const userBadges = await this.db.find({
|
||||
selector: { type: 'user_badge' },
|
||||
fields: ['userId', 'badgeId', 'earnedAt', 'progress']
|
||||
});
|
||||
|
||||
const badges = await this.db.find({
|
||||
selector: { type: 'badge' },
|
||||
fields: ['_id', 'name', 'description', 'icon', 'rarity']
|
||||
});
|
||||
|
||||
const badgeMap = {};
|
||||
badges.docs.forEach(badge => {
|
||||
badgeMap[badge._id] = {
|
||||
badgeId: badge._id,
|
||||
name: badge.name,
|
||||
description: badge.description,
|
||||
icon: badge.icon,
|
||||
rarity: badge.rarity
|
||||
};
|
||||
});
|
||||
|
||||
const userBadgeMap = {};
|
||||
userBadges.docs.forEach(userBadge => {
|
||||
if (!userBadgeMap[userBadge.userId]) {
|
||||
userBadgeMap[userBadge.userId] = [];
|
||||
}
|
||||
|
||||
const badgeInfo = badgeMap[userBadge.badgeId];
|
||||
if (badgeInfo) {
|
||||
userBadgeMap[userBadge.userId].push({
|
||||
...badgeInfo,
|
||||
earnedAt: userBadge.earnedAt,
|
||||
progress: userBadge.progress
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update users with badge info
|
||||
for (const userId in userBadgeMap) {
|
||||
const userDoc = await this.db.get(userId);
|
||||
userDoc.earnedBadges = userBadgeMap[userId];
|
||||
userDoc.stats.badgesEarned = userBadgeMap[userId].length;
|
||||
await this.db.insert(userDoc);
|
||||
}
|
||||
|
||||
console.log('✅ Statistics calculated');
|
||||
}
|
||||
|
||||
async runMigration() {
|
||||
try {
|
||||
await this.initialize();
|
||||
|
||||
// Phase 1: Migrate basic documents
|
||||
await this.migrateCollection(User, this.transformUser, 'users');
|
||||
await this.migrateCollection(Street, this.transformStreet, 'streets');
|
||||
await this.migrateCollection(Task, this.transformTask, 'tasks');
|
||||
await this.migrateCollection(Post, this.transformPost, 'posts');
|
||||
await this.migrateCollection(Event, this.transformEvent, 'events');
|
||||
await this.migrateCollection(Report, this.transformReport, 'reports');
|
||||
await this.migrateCollection(Badge, this.transformBadge, 'badges');
|
||||
await this.migrateCollection(Comment, this.transformComment, 'comments');
|
||||
await this.migrateCollection(PointTransaction, this.transformPointTransaction, 'pointTransactions');
|
||||
await this.migrateCollection(UserBadge, this.transformUserBadge, 'userBadges');
|
||||
|
||||
// Phase 2: Populate relationships
|
||||
await this.populateRelationships();
|
||||
|
||||
// Phase 3: Calculate statistics
|
||||
await this.calculateStats();
|
||||
|
||||
console.log('\n🎉 Migration completed!');
|
||||
console.log('\n📈 Migration Statistics:');
|
||||
console.log(`Users: ${this.migrationStats.users}`);
|
||||
console.log(`Streets: ${this.migrationStats.streets}`);
|
||||
console.log(`Tasks: ${this.migrationStats.tasks}`);
|
||||
console.log(`Posts: ${this.migrationStats.posts}`);
|
||||
console.log(`Events: ${this.migrationStats.events}`);
|
||||
console.log(`Reports: ${this.migrationStats.reports}`);
|
||||
console.log(`Badges: ${this.migrationStats.badges}`);
|
||||
console.log(`Comments: ${this.migrationStats.comments}`);
|
||||
console.log(`Point Transactions: ${this.migrationStats.pointTransactions}`);
|
||||
console.log(`User Badges: ${this.migrationStats.userBadges}`);
|
||||
|
||||
if (this.migrationStats.errors.length > 0) {
|
||||
console.log('\n⚠️ Errors encountered:');
|
||||
this.migrationStats.errors.forEach(error => console.log(` - ${error}`));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await mongoose.disconnect();
|
||||
console.log('🔌 Disconnected from MongoDB');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run migration if called directly
|
||||
if (require.main === module) {
|
||||
const migrator = new MongoToCouchMigrator();
|
||||
migrator.runMigration().catch(console.error);
|
||||
}
|
||||
|
||||
module.exports = MongoToCouchMigrator;
|
||||
Reference in New Issue
Block a user