diff --git a/lib/query.js b/lib/query.js index 6f2ef101fcd..aa3f2d31e75 100644 --- a/lib/query.js +++ b/lib/query.js @@ -4025,7 +4025,7 @@ Query.prototype._mergeUpdate = function(update) { if (update instanceof Query) { if (Array.isArray(this._update)) { - throw new MongooseError('Cannot mix array and object updates'); + throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this._update)}, incoming: ${_previewUpdate(update._update)})`); } if (update._update) { utils.mergeClone(this._update, update._update); @@ -4037,18 +4037,33 @@ Query.prototype._mergeUpdate = function(update) { if (this._update == null || utils.isEmptyObject(this._update)) { this._update = []; } else { - throw new MongooseError('Cannot mix array and object updates'); + throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this._update)}, incoming: ${_previewUpdate(update)})`); } } this._update = this._update.concat(update); } else { if (Array.isArray(this._update)) { - throw new MongooseError('Cannot mix array and object updates'); + throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this._update)}, incoming: ${_previewUpdate(update)})`); } utils.mergeClone(this._update, update); } }; +function _previewUpdate(update) { + const preview = util.inspect(update, { + depth: 2, + maxArrayLength: 5, + breakLength: 80, + compact: true + }); + + if (preview.length > 200) { + return `${preview.slice(0, 197)}...`; + } + + return preview; +} + /*! * ignore */ diff --git a/test/model.updateOne.test.js b/test/model.updateOne.test.js index 427d3438adf..69c2c41c770 100644 --- a/test/model.updateOne.test.js +++ b/test/model.updateOne.test.js @@ -3016,6 +3016,25 @@ describe('model: updateOne: ', function() { assert.ok(fromDb.nested.updatedAt > doc.nested.updatedAt); }); + it('includes update previews when mixing array and object updates', function() { + const schema = Schema({ name: String }); + const Model = db.model('UpdatePreview', schema); + + const currentUpdate = { $set: { name: 'A' } }; + const incomingUpdate = [{ $set: { name: 'B' } }]; + const query = Model.updateOne({ name: 'Start' }, currentUpdate); + + assert.throws(() => { + query.updateOne({ name: 'Start' }, incomingUpdate, { updatePipeline: true }); + }, err => { + assert.strictEqual( + err.message, + 'Cannot mix array and object updates (current: { \'$set\': { name: \'A\' } }, incoming: [ { \'$set\': { name: \'B\' } } ])' + ); + return true; + }); + }); + describe('mongodb 42 features', function() { before(async function() { const version = await start.mongodVersion();