Add script to fix duplicate contacts in database
This commit is contained in:
90
src/scripts/fix-duplicate-contacts.js
Normal file
90
src/scripts/fix-duplicate-contacts.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { db } from '../config/database.js';
|
||||
import { contacts, emails } from '../db/schema.js';
|
||||
import { eq, and, sql } from 'drizzle-orm';
|
||||
import { logger } from '../utils/logger.js';
|
||||
|
||||
/**
|
||||
* Fix duplicate contacts by merging them
|
||||
* - Finds contacts with the same email address
|
||||
* - Keeps the newest contact
|
||||
* - Updates all emails to use the newest contact ID
|
||||
* - Deletes old duplicate contacts
|
||||
*/
|
||||
async function fixDuplicateContacts() {
|
||||
try {
|
||||
logger.info('🔍 Finding duplicate contacts...');
|
||||
|
||||
// Find duplicate contacts (same userId + email)
|
||||
const duplicates = await db
|
||||
.select({
|
||||
userId: contacts.userId,
|
||||
email: contacts.email,
|
||||
count: sql`count(*)::int`,
|
||||
ids: sql`array_agg(${contacts.id} ORDER BY ${contacts.createdAt} DESC)`,
|
||||
})
|
||||
.from(contacts)
|
||||
.groupBy(contacts.userId, contacts.email)
|
||||
.having(sql`count(*) > 1`);
|
||||
|
||||
if (duplicates.length === 0) {
|
||||
logger.success('✅ No duplicate contacts found!');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`Found ${duplicates.length} sets of duplicate contacts`);
|
||||
|
||||
let totalFixed = 0;
|
||||
let totalDeleted = 0;
|
||||
|
||||
for (const dup of duplicates) {
|
||||
const contactIds = dup.ids;
|
||||
const newestContactId = contactIds[0]; // First one (ordered by createdAt DESC)
|
||||
const oldContactIds = contactIds.slice(1); // Rest are duplicates
|
||||
|
||||
logger.info(`\n📧 Fixing duplicates for ${dup.email}:`);
|
||||
logger.info(` - Keeping contact: ${newestContactId}`);
|
||||
logger.info(` - Merging ${oldContactIds.length} duplicate(s): ${oldContactIds.join(', ')}`);
|
||||
|
||||
// Update all emails from old contacts to use the newest contact ID
|
||||
for (const oldContactId of oldContactIds) {
|
||||
const updateResult = await db
|
||||
.update(emails)
|
||||
.set({ contactId: newestContactId, updatedAt: new Date() })
|
||||
.where(eq(emails.contactId, oldContactId))
|
||||
.returning();
|
||||
|
||||
if (updateResult.length > 0) {
|
||||
logger.success(` ✅ Updated ${updateResult.length} emails from ${oldContactId} → ${newestContactId}`);
|
||||
totalFixed += updateResult.length;
|
||||
}
|
||||
|
||||
// Delete the old duplicate contact
|
||||
await db
|
||||
.delete(contacts)
|
||||
.where(eq(contacts.id, oldContactId));
|
||||
|
||||
logger.success(` 🗑️ Deleted duplicate contact: ${oldContactId}`);
|
||||
totalDeleted++;
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`\n✅ Cleanup complete!`);
|
||||
logger.success(` - Fixed ${totalFixed} emails`);
|
||||
logger.success(` - Deleted ${totalDeleted} duplicate contacts`);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('❌ Error fixing duplicate contacts:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
fixDuplicateContacts()
|
||||
.then(() => {
|
||||
logger.success('🎉 Script completed successfully!');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('💥 Script failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user