diff --git a/CHANGLOG.md b/CHANGLOG.md index 791ba7a..3b6b95d 100644 --- a/CHANGLOG.md +++ b/CHANGLOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.7.0 (2026-06-19) + +- Patch rollback incorrectly restoring nested objects - https://github.com/360Learning/mongo-bulk-data-migration/pull/36 + ## 1.6.0 (2026-02-25) - Automatic resume for empty queries using `FETCH_ALL` - https://github.com/360Learning/mongo-bulk-data-migration/pull/31 diff --git a/__tests__/MongoBulkDataMigration.rollback.test.ts b/__tests__/MongoBulkDataMigration.rollback.test.ts index d9aff34..855a56a 100644 --- a/__tests__/MongoBulkDataMigration.rollback.test.ts +++ b/__tests__/MongoBulkDataMigration.rollback.test.ts @@ -216,6 +216,25 @@ describe('MongoBulkDataMigration', () => { expect(restoredDocuments).toEqual(insertedDocuments); }); + it('should restore a nested object value when the target key already existed', async () => { + await collection.insertOne({ + a: { source: { x: { nested: 1 } }, target: { x: { nested: 9 } } }, + }); + const insertedDocuments = await collection.find().toArray(); + const dataMigration = new MongoBulkDataMigration({ + ...DM_DEFAULT_SETUP, + projection: { a: 1 }, + query: { 'a.source': { $exists: true } }, + update: { $set: { 'a.target': { x: { nested: 1 } } } }, + }); + + await dataMigration.update(); + await dataMigration.rollback(); + + const restoredDocuments = await collection.find().toArray(); + expect(restoredDocuments).toEqual(insertedDocuments); + }); + it('should restore removed documents', async () => { await collection.insertMany([{ key: 1 }, { key: 2 }, { key: 3 }]); const insertedDocuments = await collection.find().toArray(); diff --git a/__tests__/lib/computeRollbackQuery.unit.ts b/__tests__/lib/computeRollbackQuery.unit.ts index add3582..a2cccaf 100644 --- a/__tests__/lib/computeRollbackQuery.unit.ts +++ b/__tests__/lib/computeRollbackQuery.unit.ts @@ -54,6 +54,19 @@ describe('computeRollbackQuery', () => { $set: { 'nested.array': ['a', 'b'] }, }); }); + + it('should set back the nested original object value when the target already existed', async () => { + const updateQuery = { + $set: { 'a.b': { channels: { email: false, teams: true } } }, + }; + const backup = { a: { b: { channels: { email: true } } } }; + + const restoreQuery = computeRollbackQuery(updateQuery, backup); + + expect(restoreQuery).toEqual({ + $set: { 'a.b': { channels: { email: true } } }, + }); + }); }); describe('resulting from an $unset', () => { diff --git a/package.json b/package.json index 6508606..7c967ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@360-l/mongo-bulk-data-migration", - "version": "1.6.0", + "version": "1.7.0", "description": "MongoDB bulk data migration for node scripts", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/lib/computeRollbackQuery.ts b/src/lib/computeRollbackQuery.ts index 275b96c..78bb472 100644 --- a/src/lib/computeRollbackQuery.ts +++ b/src/lib/computeRollbackQuery.ts @@ -115,11 +115,10 @@ function computeRollbackSet( (setProperty) => key.startsWith(`${setProperty}.`), ); if (setPropertyKeyStartsWith) { - const keyToSet = key.slice(setPropertyKeyStartsWith.length + 1); - rollbackSet[setPropertyKeyStartsWith] = { - ...(rollbackSet[setPropertyKeyStartsWith] ?? {}), - [keyToSet]: value, - }; + rollbackSet[setPropertyKeyStartsWith] = _.get( + backup, + setPropertyKeyStartsWith, + ); return rollbackSet; }