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
JavaScript
;
/* 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