/** * MongoDB-Export → MySQL Migration * * Liest eine NDJSON-Datei (mongoexport-Format, eine Zeile = ein Dokument) * und importiert alle Einträge in die MySQL-Tabelle. * * Konfiguration via Umgebungsvariablen oder direkt hier eintragen: * * IMPORT_FILE Pfad zur Exportdatei (default: ~/Downloads/spritzschema_a) * MYSQLHOST MySQL-Host (default: localhost) * MYSQLPORT MySQL-Port (default: 3306) * MYSQLUSER MySQL-Benutzer (default: root) * MYSQLPASSWORD MySQL-Passwort (default: leer) * MYSQLBASE MySQL-Datenbank (default: medizin) * MYSQL_TABLE MySQL-Tabellenname (default: spritzschema) * * Aufruf: * node scripts/migrate-mongo-to-mysql.mjs * IMPORT_FILE=/pfad/zur/datei node scripts/migrate-mongo-to-mysql.mjs */ import fs from 'fs'; import os from 'os'; import path from 'path'; import mysql from 'mysql2/promise'; // ── .env.local laden (falls vorhanden) ──────────────────────── const envFile = path.resolve(process.cwd(), '.env.local'); if (fs.existsSync(envFile)) { for (const line of fs.readFileSync(envFile, 'utf-8').split('\n')) { const m = line.match(/^\s*([A-Z_]+)\s*=\s*(.*)\s*$/); if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^['"]|['"]$/g, ''); } } // ── Konfiguration ────────────────────────────────────────────── const IMPORT_FILE = process.env.IMPORT_FILE || path.join(os.homedir(), 'Downloads', 'spritzschema_a'); const MYSQLHOST = process.env.MYSQLHOST || 'localhost'; const MYSQLPORT = parseInt(process.env.MYSQLPORT || '3306'); const MYSQLUSER = process.env.MYSQLUSER || 'root'; const MYSQLPASSWORD = process.env.MYSQLPASSWORD || ''; const MYSQLBASE = process.env.MYSQLBASE || 'medizin'; const MYSQL_TABLE = process.env.MYSQL_TABLE || 'spritzschema'; // ─────────────────────────────────────────────────────────────── async function ensureTable(conn) { await conn.query(` CREATE TABLE IF NOT EXISTS \`${MYSQL_TABLE}\` ( id INT AUTO_INCREMENT PRIMARY KEY, curdate VARCHAR(20) NOT NULL UNIQUE, months JSON NOT NULL, years JSON NOT NULL, data JSON NOT NULL, einheit INT NOT NULL DEFAULT 0, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) `); } function normalizeDoc(doc) { // einheit in data-Items kann String oder Number sein → immer Int const data = (Array.isArray(doc.data) ? doc.data : []).map((item) => ({ ...item, einheit: parseInt(item.einheit) || 0, })); return { curdate: doc.curdate ?? '', months: Array.isArray(doc.months) ? doc.months : [], years: Array.isArray(doc.years) ? doc.years : [], data, einheit: parseInt(doc.einheit) || 0, }; } async function readExportFile(filePath) { const content = fs.readFileSync(filePath, 'utf-8').trim(); // Unterstützt beide Formate: JSON-Array [...] und NDJSON (eine Zeile = ein Dokument) if (content.startsWith('[')) { return JSON.parse(content); } // NDJSON-Fallback const docs = []; let lineNo = 0; for (const line of content.split('\n')) { lineNo++; const trimmed = line.trim(); if (!trimmed) continue; try { docs.push(JSON.parse(trimmed)); } catch (e) { console.warn(` SKIP Zeile ${lineNo}: Kein gültiges JSON (${e.message})`); } } return docs; } async function migrate() { // ── Exportdatei lesen ── console.log(`Lese Exportdatei: ${IMPORT_FILE}`); if (!fs.existsSync(IMPORT_FILE)) { console.error(`FEHLER: Datei nicht gefunden: ${IMPORT_FILE}`); process.exit(1); } const docs = await readExportFile(IMPORT_FILE); console.log(` ${docs.length} Dokument(e) eingelesen.`); if (docs.length === 0) { console.log('Nichts zu migrieren.'); return; } // ── MySQL verbinden ── console.log(`MySQL verbinden: ${MYSQLHOST}:${MYSQLPORT} / ${MYSQLBASE}`); const conn = await mysql.createConnection({ host: MYSQLHOST, port: MYSQLPORT, user: MYSQLUSER, password: MYSQLPASSWORD, database: MYSQLBASE, }); await ensureTable(conn); console.log(` Tabelle '${MYSQL_TABLE}' bereit.`); let inserted = 0; let updated = 0; let errors = 0; for (const doc of docs) { const row = normalizeDoc(doc); if (!row.curdate) { console.warn(` SKIP: Dokument ohne curdate (_id=${doc._id})`); errors++; continue; } try { const [result] = await conn.query( `INSERT INTO \`${MYSQL_TABLE}\` (curdate, months, years, data, einheit) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE months = VALUES(months), years = VALUES(years), data = VALUES(data), einheit = VALUES(einheit), updated_at = CURRENT_TIMESTAMP`, [ row.curdate, JSON.stringify(row.months), JSON.stringify(row.years), JSON.stringify(row.data), row.einheit, ] ); // affectedRows=1 → INSERT, affectedRows=2 → UPDATE (ON DUPLICATE KEY) if (result.affectedRows === 1) { console.log(` INSERT curdate=${row.curdate}`); inserted++; } else { console.log(` UPDATE curdate=${row.curdate}`); updated++; } } catch (e) { console.error(` FEHLER curdate=${row.curdate}: ${e.message}`); errors++; } } await conn.end(); console.log('\n── Ergebnis ──────────────────────────'); console.log(` Inserted : ${inserted}`); console.log(` Updated : ${updated}`); console.log(` Errors : ${errors}`); console.log(` Gesamt : ${docs.length}`); } migrate().catch((err) => { console.error('Fataler Fehler:', err); process.exit(1); });