EventBuzz_Backend/scripts/migrate-participants-multi-...

181 lines
4.7 KiB
JavaScript

/**
* One-time migration: legacy Participant (single `event` ref) → tenantOrganization + eventMemberships[].
* Merges legacy rows that share the same (organization + email) into one document.
*
* Usage (from backend/): node scripts/migrate-participants-multi-event.js
*/
require('dotenv').config();
const mongoose = require('mongoose');
const Event = require('../src/models/Event');
const LEGACY_COLLECTION = 'participants';
function pickIdentity(doc) {
const keys = [
'email',
'firstName',
'lastName',
'displayName',
'phone',
'organization',
'designation',
'customFields',
'payment',
'notes',
'emailVerified',
];
const out = {};
for (const k of keys) {
if (doc[k] !== undefined) out[k] = doc[k];
}
return out;
}
function mergeIdentity(base, next) {
const out = { ...base };
for (const [k, v] of Object.entries(next)) {
if (v === undefined || v === null) continue;
if (typeof v === 'string' && !v.trim()) continue;
if (out[k] === undefined || out[k] === null || out[k] === '') {
out[k] = v;
}
}
return out;
}
async function main() {
const uri = process.env.MONGODB_URI || process.env.MONGO_URI;
if (!uri) {
console.error('Set MONGODB_URI or MONGO_URI');
process.exit(1);
}
await mongoose.connect(uri);
const coll = mongoose.connection.db.collection(LEGACY_COLLECTION);
const legacy = await coll
.find({
event: { $exists: true, $ne: null },
$or: [{ eventMemberships: { $exists: false } }, { eventMemberships: { $size: 0 } }],
})
.toArray();
console.log(`Found ${legacy.length} legacy participant documents`);
/** @type {Map<string, { keepId: any, docIds: any[], memberships: any[], identity: object }>} */
const emailGroups = new Map();
const singles = [];
for (const doc of legacy) {
const ev = await Event.findById(doc.event).lean();
if (!ev?.organization) {
console.warn('Skipping participant (no event org)', String(doc._id));
continue;
}
const membership = {
event: doc.event,
status: doc.status || 'pending',
checkedIn: Boolean(doc.checkedIn),
checkedInAt: doc.checkedInAt || undefined,
guestToken: doc.guestToken || undefined,
registrationDate: doc.registrationDate || doc.createdAt || new Date(),
tags: Array.isArray(doc.tags) ? [...doc.tags] : [],
};
const email = String(doc.email || '').trim().toLowerCase();
if (!email) {
singles.push({
docId: doc._id,
tenantOrganization: ev.organization,
membership,
identity: pickIdentity(doc),
});
continue;
}
const key = `${String(ev.organization)}|${email}`;
if (!emailGroups.has(key)) {
emailGroups.set(key, {
keepId: doc._id,
docIds: [],
memberships: [],
identity: pickIdentity(doc),
});
}
const g = emailGroups.get(key);
g.docIds.push(doc._id);
g.memberships.push(membership);
g.identity = mergeIdentity(g.identity, pickIdentity(doc));
if (String(doc._id) < String(g.keepId)) g.keepId = doc._id;
}
for (const [, g] of emailGroups) {
const ev0 = await Event.findById(g.memberships[0].event).lean();
const tenantOrganization = ev0?.organization;
if (!tenantOrganization) continue;
const byEvent = new Map();
for (const m of g.memberships) {
byEvent.set(String(m.event), m);
}
const eventMemberships = [...byEvent.values()];
await coll.updateOne(
{ _id: g.keepId },
{
$set: {
...g.identity,
email: String(g.identity.email || '').trim().toLowerCase(),
tenantOrganization,
eventMemberships,
},
$unset: {
event: '',
status: '',
checkedIn: '',
checkedInAt: '',
guestToken: '',
registrationDate: '',
tags: '',
},
}
);
const others = g.docIds.filter((id) => String(id) !== String(g.keepId));
if (others.length) {
await coll.deleteMany({ _id: { $in: others } });
console.log('Merged duplicates:', others.length, 'removed; kept', String(g.keepId));
}
}
for (const s of singles) {
await coll.updateOne(
{ _id: s.docId },
{
$set: {
...s.identity,
tenantOrganization: s.tenantOrganization,
eventMemberships: [s.membership],
},
$unset: {
event: '',
status: '',
checkedIn: '',
checkedInAt: '',
guestToken: '',
registrationDate: '',
tags: '',
},
}
);
}
console.log('Done. Email groups:', emailGroups.size, 'guest rows:', singles.length);
await mongoose.disconnect();
}
main().catch((e) => {
console.error(e);
process.exit(1);
});