The task was to migrate some documents in a mongodb collection:
- find a document which have a particular old name
- create modifications (the property values for tariff and tariffTable)
- update the existing document with the new property values
The following code was supposed to do the job:
const MongoClient = require('mongodb').MongoClient; const mongoClient = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }); const offerCollection = mongoClient.db('offermatrix').collection('offers'); const oldName = 'my ALL in One', newName = 'my All in One'; const offerCursor = offerCollection.find({ tariff: oldName }); console.log('Found %s offers with tariff name %s.', await offerCursor.count(), oldName); offerCursor.rewind(); offerCursor.limit(1); while (await offerCursor.hasNext()) { const offer = await offerCursor.next(); console.log('%s has tariff names %s.', offer.promoCode, offer.tariff); const updateDoc = { tariff: _.map(offer.tariff, tariffName => tariffName === oldName ? newName : tariffName), tariffTable: _.map(offer.tariffTable, tt => tt.tariff === oldName ? _.assign(tt, { tariff: newName }) : tt), }; console.log(updateDoc); const result = await offerCollection.updateOne(offer, { '$set': updateDoc }); console.log(result.result); } await offerCursor.close(); await Promise.all([mongoClient.close(false)]);
To my surprise the document is not being updated (the result is { n: 0, nModified: 0, ok: 1 }), so how is it possible that the very same document given by the cursor is not there any more?!
The problem is that the assign method from lodash is actually mutating the first argument, so the document object is not the very same as at the point the cursor gave it out.
In this case the remedy is simple: use the offer id as a selector (instead of the whole document). I find it still quite amazing how mutation can cause the problem, even in such a short code segments where most of us believe to know what is going on.