/** * Sync only relationships to cloud (nodes already synced) */ import neo4j, { Driver } from 'neo4j-driver'; import { config } from 'dotenv'; import { resolve } from 'path'; config({ path: resolve(process.cwd(), '.env.production') }); config({ path: resolve(process.cwd(), '.env') }); const BATCH_SIZE = 500; async function main() { const localDriver = neo4j.driver( 'bolt://localhost:7687', neo4j.auth.basic('neo4j', 'password') ); const cloudDriver = neo4j.driver( process.env.NEO4J_URI || '', neo4j.auth.basic(process.env.NEO4J_USER || 'neo4j', process.env.NEO4J_PASSWORD || '') ); console.log('═'.repeat(60)); console.log('šŸ”— Syncing Relationships Only'); console.log('═'.repeat(60)); const localSession = localDriver.session(); const countResult = await localSession.run('MATCH ()-[r]->() RETURN count(r) as count'); const totalRels = countResult.records[0].get('count').toNumber(); await localSession.close(); console.log(`\nTotal relationships to sync: ${totalRels}`); let offset = 0; let processed = 0; const startTime = Date.now(); while (offset < totalRels) { const session = localDriver.session(); const result = await session.run(` MATCH (a)-[r]->(b) RETURN type(r) as type, properties(r) as props, elementId(a) as startId, elementId(b) as endId SKIP $skip LIMIT $limit `, { skip: neo4j.int(offset), limit: neo4j.int(BATCH_SIZE) }); const rels = result.records.map(r => ({ type: r.get('type') as string, properties: r.get('props') as Record, startId: r.get('startId') as string, endId: r.get('endId') as string })); await session.close(); if (rels.length === 0) break; // Group by type const relsByType = new Map(); for (const rel of rels) { if (!relsByType.has(rel.type)) { relsByType.set(rel.type, []); } relsByType.get(rel.type)!.push(rel); } // Import each type for (const [relType, typeRels] of relsByType) { const cloudSession = cloudDriver.session(); try { const relData = typeRels.map(r => ({ startId: r.startId, endId: r.endId, props: r.properties })); await cloudSession.run(` UNWIND $rels as relData MATCH (a {_syncId: relData.startId}) MATCH (b {_syncId: relData.endId}) MERGE (a)-[r:${relType}]->(b) SET r = relData.props `, { rels: relData }); } finally { await cloudSession.close(); } } processed += rels.length; offset += BATCH_SIZE; const elapsed = (Date.now() - startTime) / 1000; const rate = Math.round(processed / elapsed); const eta = Math.round((totalRels - processed) / rate); console.log(` Progress: ${processed}/${totalRels} (${rate} rels/sec, ETA: ${eta}s)`); } console.log(`\nāœ… Synced ${processed} relationships in ${((Date.now() - startTime) / 1000).toFixed(1)}s`); await localDriver.close(); await cloudDriver.close(); } main().catch(console.error);