Files
spritzschema-next/scripts/migrate-mongo-to-mysql.mjs
2026-03-16 09:20:30 +01:00

185 lines
6.0 KiB
JavaScript

/**
* 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);
});