185 lines
6.0 KiB
JavaScript
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);
|
|
});
|