UNPKG

morpheus4j

Version:

Morpheus is a migration tool for Neo4j. It aims to be a simple and intuitive way to migrate your database.

217 lines 7.93 kB
"use strict"; /* eslint-disable no-await-in-loop */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Repository = void 0; const constants_1 = require("../constants"); const logger_1 = require("./logger"); class Repository { driver; constructor(driver) { this.driver = driver; } async applyMigration(migration, fromVersion, duration) { const queries = [ { parameters: { duration, fromVersion, properties: migration, }, statement: ` MATCH (m:${constants_1.MIGRATION_LABEL} {version: $fromVersion}) CREATE (m)-[r:MIGRATED_TO]->(new:${constants_1.MIGRATION_LABEL} $properties) SET r.at = datetime({timezone: 'UTC'}), r.in = duration({milliseconds: $duration}) `, }, ]; await this.executeQueries(queries); } async cleanConstraints() { const schemaQueries = [ { statement: `DROP CONSTRAINT unique_version_${constants_1.MIGRATION_LABEL} IF EXISTS` }, { statement: `DROP INDEX idx_version_${constants_1.MIGRATION_LABEL} IF EXISTS` }, ]; await this.executeQueries(schemaQueries); } async cleanMigrations() { const dataQueries = [ { statement: `MATCH (:${constants_1.MIGRATION_LABEL})-[r:MIGRATED_TO]->(m:${constants_1.MIGRATION_LABEL}) DETACH DELETE m, r` }, { statement: `MATCH (b:${constants_1.MIGRATION_LABEL}) DETACH DELETE b` }, ]; await this.executeQueries(dataQueries); } async createMigrationRelation(fromVersion, toVersion) { const queries = [ { parameters: { fromVersion, toVersion }, statement: ` MATCH (prev:${constants_1.MIGRATION_LABEL} {version: $fromVersion}) MATCH (next:${constants_1.MIGRATION_LABEL} {version: $toVersion}) MERGE (prev)-[r:MIGRATED_TO]->(next) SET r.at = datetime({timezone: 'UTC'}) SET r.in = duration({ months: 0, days: 0, seconds: 0, nanoseconds: 0 }) `, }, ]; await this.executeQueries(queries); } async deleteMigration(version) { const queries = [ { parameters: { version }, statement: ` MATCH (m:${constants_1.MIGRATION_LABEL} {version: $version}) DETACH DELETE m `, }, ]; await this.executeQueries(queries); } async executeQueries(queries) { const session = this.driver.session(); const transaction = session.beginTransaction(); try { for (const query of queries) { logger_1.Logger.debug(`Executing query: ${query.statement}`); if (query.parameters) { logger_1.Logger.debug(`With parameters: ${JSON.stringify(query.parameters)}`); } await transaction.run(query.statement, query.parameters || {}); } await transaction.commit(); } catch (error) { await transaction.rollback(); throw error; } finally { await session.close(); } } async executeQuery(query) { const session = this.driver.session(); const transaction = session.beginTransaction(); try { logger_1.Logger.debug(`Executing query: ${query.statement}`); if (query.parameters) { logger_1.Logger.debug(`With parameters: ${JSON.stringify(query.parameters)}`); } await transaction.run(query.statement, query.parameters || {}); await transaction.commit(); } catch (error) { await transaction.rollback(); throw error; } finally { await session.close(); } } async getMigrationInfo(version) { const session = this.driver.session(); try { const result = await session.run(` MATCH (m:${constants_1.MIGRATION_LABEL} {version: $version}) OPTIONAL MATCH (prev:${constants_1.MIGRATION_LABEL})-[r:MIGRATED_TO]->(m) RETURN m, r `, { version }); if (result.records.length === 0) { return null; } const record = result.records[0]; const node = record.get('m').properties; const relation = record.get('r')?.properties; return { node, relation, }; } finally { await session.close(); } } async getMigrationState() { const queries = [ { parameters: { version: constants_1.BASELINE }, statement: `MATCH (base:${constants_1.MIGRATION_LABEL} {version: $version}) RETURN base`, }, { statement: ` MATCH (m:${constants_1.MIGRATION_LABEL}) WHERE NOT (m)-[:MIGRATED_TO]->(:${constants_1.MIGRATION_LABEL}) RETURN m LIMIT 1 `, }, { parameters: { baseline: constants_1.BASELINE }, statement: ` MATCH p=(base:${constants_1.MIGRATION_LABEL} {version: $baseline})-[:MIGRATED_TO*]->(m:${constants_1.MIGRATION_LABEL}) WITH m, relationships(p) as rels ORDER BY m.version RETURN m, LAST(rels) as r `, }, ]; const session = this.driver.session(); try { const baselineExistsResult = await session.run(queries[0].statement, queries[0].parameters); const latestMigrationResult = await session.run(queries[1].statement, queries[1].parameters); const migrationsResult = await session.run(queries[2].statement, queries[2].parameters); return { appliedMigrations: migrationsResult.records.map((record) => ({ node: record.get('m').properties, relation: record.get('r')?.properties || null, })), baselineExists: baselineExistsResult.records.length > 0, latestMigration: latestMigrationResult.records[0]?.get('m').properties || null, }; } finally { await session.close(); } } async initializeBaseline() { const queries = [ { parameters: { version: constants_1.BASELINE }, statement: `CREATE (base:${constants_1.MIGRATION_LABEL} {version: $version})`, }, ]; await this.executeQueries(queries); } async initializeSchema() { const schemaQueries = [ { statement: `CREATE CONSTRAINT unique_version_${constants_1.MIGRATION_LABEL} IF NOT EXISTS FOR (m:${constants_1.MIGRATION_LABEL}) REQUIRE m.version IS UNIQUE`, }, { statement: `CREATE INDEX idx_version_${constants_1.MIGRATION_LABEL} IF NOT EXISTS FOR (m:${constants_1.MIGRATION_LABEL}) ON (m.version)`, }, ]; await this.executeQueries(schemaQueries); } async markAsLatestMigration(version) { const queries = [ { statement: ` MATCH (m:${constants_1.MIGRATION_LABEL}) WHERE m.isLatest IS NOT NULL REMOVE m.isLatest `, }, { parameters: { version }, statement: ` MATCH (m:${constants_1.MIGRATION_LABEL} {version: $version}) SET m.isLatest = true `, }, ]; await this.executeQueries(queries); } } exports.Repository = Repository; //# sourceMappingURL=neo4j.repository.js.map